Conversions to H4 formats
authorMichael M Slusarz <slusarz@curecanti.org>
Tue, 9 Feb 2010 18:34:20 +0000 (11:34 -0700)
committerMichael M Slusarz <slusarz@curecanti.org>
Tue, 9 Feb 2010 19:28:16 +0000 (12:28 -0700)
74 files changed:
ingo/blacklist.php
ingo/config/fields.php.dist
ingo/config/prefs.php.dist
ingo/filters.php
ingo/forward.php
ingo/lib/Api.php
ingo/lib/Application.php
ingo/lib/Driver.php
ingo/lib/Driver/Ldap.php
ingo/lib/Driver/Sivtest.php
ingo/lib/Driver/Timsieved.php
ingo/lib/Driver/Vfs.php
ingo/lib/Exception.php [new file with mode: 0644]
ingo/lib/Ingo.php
ingo/lib/Script.php
ingo/lib/Script/Imap.php
ingo/lib/Script/Maildrop.php
ingo/lib/Script/Maildrop/Comment.php [new file with mode: 0644]
ingo/lib/Script/Maildrop/Recipe.php [new file with mode: 0644]
ingo/lib/Script/Maildrop/Variable.php [new file with mode: 0644]
ingo/lib/Script/Procmail.php
ingo/lib/Script/Procmail/Comment.php [new file with mode: 0644]
ingo/lib/Script/Procmail/Recipe.php [new file with mode: 0644]
ingo/lib/Script/Procmail/Variable.php [new file with mode: 0644]
ingo/lib/Script/Sieve.php
ingo/lib/Script/Sieve/Action.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Action/Addflag.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Action/Discard.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Action/Fileinto.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Action/Flag.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Action/Keep.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Action/Notify.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Action/Redirect.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Action/Reject.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Action/Removeflag.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Action/Stop.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Action/Vacation.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Comment.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Else.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Elsif.php [new file with mode: 0644]
ingo/lib/Script/Sieve/If.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Sieve.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Test.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Test/Address.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Test/Allof.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Test/Anyof.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Test/Body.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Test/Exists.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Test/False.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Test/Header.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Test/Not.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Test/Relational.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Test/Size.php [new file with mode: 0644]
ingo/lib/Script/Sieve/Test/True.php [new file with mode: 0644]
ingo/lib/Session.php [deleted file]
ingo/lib/Storage.php
ingo/lib/Storage/Blacklist.php
ingo/lib/Storage/Forward.php
ingo/lib/Storage/Mock.php
ingo/lib/Storage/Rule.php
ingo/lib/Storage/Spam.php
ingo/lib/Storage/Sql.php
ingo/lib/Storage/Vacation.php
ingo/lib/Storage/VacationTest.php
ingo/lib/Storage/Whitelist.php
ingo/rule.php
ingo/script.php
ingo/scripts/ingo-postfix-policyd
ingo/scripts/upgrades/convert_prefs_to_sql.php
ingo/spam.php
ingo/templates/rule/filter.inc
ingo/templates/rule/header.inc
ingo/vacation.php
ingo/whitelist.php

index 291e2eb..367ba43 100644 (file)
@@ -28,22 +28,23 @@ $scriptor = Ingo::loadIngoScript();
 $have_mark = $scriptor && in_array(Ingo_Storage::ACTION_FLAGONLY, $scriptor->availableActions());
 
 /* Get the blacklist object. */
-$blacklist = &$ingo_storage->retrieve(Ingo_Storage::ACTION_BLACKLIST);
-if (is_a($blacklist, 'PEAR_Error')) {
-    $notification->push($blacklist);
+try {
+    $blacklist = $ingo_storage->retrieve(Ingo_Storage::ACTION_BLACKLIST);
+} catch (Ingo_Exception $e) {
+    $notification->push($e);
     $blacklist = new Ingo_Storage_Blacklist();
 }
 $folder = $blacklist_folder = null;
 
 /* Perform requested actions. */
-$actionID = Horde_Util::getFormData('actionID');
-switch ($actionID) {
+$vars = Horde_Variables::getDefaultVariables();
+switch ($vars->actionID) {
 case 'create_folder':
-    $blacklist_folder = Ingo::createFolder(Horde_Util::getFormData('new_folder_name'));
+    $blacklist_folder = Ingo::createFolder($vars->new_folder_name);
     break;
 
 case 'rule_update':
-    switch (Horde_Util::getFormData('action')) {
+    switch ($vars->action) {
     case 'delete':
         $folder = '';
         break;
@@ -53,14 +54,14 @@ case 'rule_update':
         break;
 
     case 'folder':
-        $folder = Horde_Util::getFormData('actionvalue');
+        $folder = $vars->actionvalue;
         break;
     }
 
     if (($folder == Ingo::BLACKLIST_MARKER) && !$have_mark) {
         $notification->push("Not supported by this script generator.", 'horde.error');
     } else {
-        $ret = $blacklist->setBlacklist(Horde_Util::getFormData('blacklist'));
+        $ret = $blacklist->setBlacklist($vars->blacklist);
         if (is_a($ret, 'PEAR_Error')) {
             $notification->push($ret, $ret->getCode());
         } else {
@@ -94,7 +95,7 @@ $folder_list = Ingo::flistSelect($blacklist_folder, 'filters', 'actionvalue',
                                  '].checked=true');
 
 /* Get the blacklist rule. */
-$filters = &$ingo_storage->retrieve(Ingo_Storage::ACTION_FILTERS);
+$filters = $ingo_storage->retrieve(Ingo_Storage::ACTION_FILTERS);
 $bl_rule = $filters->findRule(Ingo_Storage::ACTION_BLACKLIST);
 
 Ingo::prepareMenu();
index 2c77ff6..e6cf33d 100644 (file)
  * $Id$
  */
 $ingo_fields = array(
-  'To' => array(
-    'label' => _("To"),
-    'type' => Ingo_Storage::TYPE_HEADER
-  ),
-  'Subject' => array(
-    'label' => _("Subject"),
-    'type' => Ingo_Storage::TYPE_HEADER
-  ),
-  'Sender' => array(
-    'label' => _("Sender"),
-    'type' => Ingo_Storage::TYPE_HEADER
-  ),
-  'From' => array(
-    'label' => _("From"),
-    'type' => Ingo_Storage::TYPE_HEADER
-  ),
-  'Cc' => array(
-    'label' => _("Cc"),
-    'type' => Ingo_Storage::TYPE_HEADER
-  ),
-  'Bcc' => array(
-    'label' => _("Bcc"),
-    'type' => Ingo_Storage::TYPE_HEADER
-  ),
-  'Resent-from' => array(
-    'label' => _("Resent-From"),
-    'type' => Ingo_Storage::TYPE_HEADER
-  ),
-  'Resent-to' => array(
-    'label' => _("Resent-To"),
-    'type' => Ingo_Storage::TYPE_HEADER
-  ),
-  'List-Id' => array(
-    'label' => _("List-ID"),
-    'type' => Ingo_Storage::TYPE_HEADER
-  ),
-  'Received' => array(
-    'label' => _("Received"),
-    'type' => Ingo_Storage::TYPE_HEADER
-  ),
-  'X-Spam-Level' => array(
-    'label' => _("X-Spam-Level"),
-    'type' => Ingo_Storage::TYPE_HEADER
-  ),
-  'X-Spam-Score' => array(
-    'label' => _("X-Spam-Score"),
-    'type' => Ingo_Storage::TYPE_HEADER
-  ),
-  'X-Spam-Status' => array(
-    'label' => _("X-Spam-Status"),
-    'type' => Ingo_Storage::TYPE_HEADER
-  ),
-  'X-Priority' => array(
-    'label' => _("X-Priority"),
-    'type' => Ingo_Storage::TYPE_HEADER
-  ),
-  'To,Cc,Bcc,Resent-to' => array(
-    'label' => _("Destination (To, Cc, Bcc, etc.)"),
-    'type' => Ingo_Storage::TYPE_HEADER
-  ),
-  'From,Sender,Reply-to,Resent-from' => array(
-    'label' => _("Source (From, Reply-to, etc.)"),
-    'type' => Ingo_Storage::TYPE_HEADER
-  ),
-  'To,Cc,Bcc,Resent-to,From,Sender,Reply-to,Resent-from' => array(
-    'label' => _("Participant (From, To, etc.)"),
-    'type' => Ingo_Storage::TYPE_HEADER
-  ),
-  'Size' => array(
-    'label' => _("Size"),
-    'type' => Ingo_Storage::TYPE_SIZE,
-    'tests' => array('greater than', 'less than')
-  ),
-  'Body' => array(
-    'label' => _("Body"),
-    'type' => Ingo_Storage::TYPE_BODY,
-    'tests' => array('contains', 'not contain', 'is', 'not is', 'begins with',
-                     'not begins with', 'ends with', 'not ends with', 'regex',
-                     'matches', 'not matches')
-  )
+    'To' => array(
+        'label' => _("To"),
+        'type' => Ingo_Storage::TYPE_HEADER
+    ),
+    'Subject' => array(
+        'label' => _("Subject"),
+        'type' => Ingo_Storage::TYPE_HEADER
+    ),
+    'Sender' => array(
+        'label' => _("Sender"),
+        'type' => Ingo_Storage::TYPE_HEADER
+    ),
+    'From' => array(
+        'label' => _("From"),
+        'type' => Ingo_Storage::TYPE_HEADER
+    ),
+    'Cc' => array(
+        'label' => _("Cc"),
+        'type' => Ingo_Storage::TYPE_HEADER
+    ),
+    'Bcc' => array(
+        'label' => _("Bcc"),
+        'type' => Ingo_Storage::TYPE_HEADER
+    ),
+    'Resent-from' => array(
+        'label' => _("Resent-From"),
+        'type' => Ingo_Storage::TYPE_HEADER
+    ),
+    'Resent-to' => array(
+        'label' => _("Resent-To"),
+        'type' => Ingo_Storage::TYPE_HEADER
+    ),
+    'List-Id' => array(
+        'label' => _("List-ID"),
+        'type' => Ingo_Storage::TYPE_HEADER
+    ),
+    'Received' => array(
+        'label' => _("Received"),
+        'type' => Ingo_Storage::TYPE_HEADER
+    ),
+    'X-Spam-Level' => array(
+        'label' => _("X-Spam-Level"),
+        'type' => Ingo_Storage::TYPE_HEADER
+    ),
+    'X-Spam-Score' => array(
+        'label' => _("X-Spam-Score"),
+        'type' => Ingo_Storage::TYPE_HEADER
+    ),
+    'X-Spam-Status' => array(
+        'label' => _("X-Spam-Status"),
+        'type' => Ingo_Storage::TYPE_HEADER
+    ),
+    'X-Priority' => array(
+        'label' => _("X-Priority"),
+        'type' => Ingo_Storage::TYPE_HEADER
+    ),
+    'To,Cc,Bcc,Resent-to' => array(
+        'label' => _("Destination (To, Cc, Bcc, etc.)"),
+        'type' => Ingo_Storage::TYPE_HEADER
+    ),
+    'From,Sender,Reply-to,Resent-from' => array(
+        'label' => _("Source (From, Reply-to, etc.)"),
+        'type' => Ingo_Storage::TYPE_HEADER
+    ),
+    'To,Cc,Bcc,Resent-to,From,Sender,Reply-to,Resent-from' => array(
+        'label' => _("Participant (From, To, etc.)"),
+        'type' => Ingo_Storage::TYPE_HEADER
+    ),
+    'Size' => array(
+        'label' => _("Size"),
+        'type' => Ingo_Storage::TYPE_SIZE,
+        'tests' => array('greater than', 'less than')
+    ),
+    'Body' => array(
+        'label' => _("Body"),
+        'type' => Ingo_Storage::TYPE_BODY,
+        'tests' => array(
+            'contains', 'not contain', 'is', 'not is', 'begins with',
+            'not begins with', 'ends with', 'not ends with', 'regex',
+            'matches', 'not matches'
+        )
+    )
 );
index d0264bb..2b16d00 100644 (file)
@@ -66,45 +66,45 @@ $_prefs['rules'] = array(
 );
 
 // Blacklist.
-// Lock this preference to disable blacklists.
 $_prefs['blacklist'] = array(
     'value' => 'a:2:{s:1:"a";a:0:{}s:1:"f";s:0:"";}',
+    // Lock this preference to disable blacklists.
     'locked' => false,
     'shared' => false,
     'type' => 'implicit'
 );
 
 // Whitelist.
-// Lock this preference to disable whitelists.
 $_prefs['whitelist'] = array(
     'value' => 'a:0:{}',
+    // Lock this preference to disable whitelists.
     'locked' => false,
     'shared' => false,
     'type' => 'implicit'
 );
 
 // Vacation notices.
-// Lock this preference to disable vacation notices.
 $_prefs['vacation'] = array(
     'value' => 'a:8:{s:9:"addresses";a:0:{}s:4:"days";i:7;s:8:"excludes";a:0:{}s:10:"ignorelist";b:1;s:6:"reason";s:0:"";s:7:"subject";s:0:"";s:5:"start";i:0;s:3:"end";i:0;}',
+    // Lock this preference to disable vacation notices.
     'locked' => false,
     'shared' => false,
     'type' => 'implicit'
 );
 
 // Forwarding.
-// Lock this preference to disable forwarding.
 $_prefs['forward'] = array(
     'value' => 'a:2:{s:1:"a";a:0:{}s:1:"k";i:0;}',
+    // Lock this preference to disable forwarding.
     'locked' => false,
     'shared' => false,
     'type' => 'implicit'
 );
 
 // Spam rule.
-// Lock this preference to disable the spam rule.
 $_prefs['spam'] = array(
     'value' => 'a:2:{s:6:"folder";N;s:5:"level";i:5;}',
+    // Lock this preference to disable the spam rule.
     'locked' => false,
     'shared' => false,
     'type' => 'implicit'
index a8f5b9e..4b3d7c8 100644 (file)
@@ -14,10 +14,7 @@ require_once dirname(__FILE__) . '/lib/Application.php';
 Horde_Registry::appInit('ingo');
 
 /* Get the list of filter rules. */
-$filters = &$ingo_storage->retrieve(Ingo_Storage::ACTION_FILTERS);
-if (is_a($filters, 'PEAR_Error')) {
-    throw new Horde_Exception($filters);
-}
+$filters = $ingo_storage->retrieve(Ingo_Storage::ACTION_FILTERS);
 
 /* Load the Ingo_Script:: driver. */
 $ingo_script = Ingo::loadIngoScript();
@@ -26,15 +23,14 @@ $ingo_script = Ingo::loadIngoScript();
 $on_demand = $ingo_script->performAvailable();
 
 /* Get web parameter data. */
-$actionID = Horde_Util::getFormData('actionID');
-$id = Horde_Util::getFormData('rulenumber');
+$vars = Horde_Variables::getDefaultVariables();
 
 /* Get permissions. */
 $edit_allowed = Ingo::hasSharePermission(Horde_Perms::EDIT);
 $delete_allowed = Ingo::hasSharePermission(Horde_Perms::DELETE);
 
 /* Perform requested actions. */
-switch ($actionID) {
+switch ($vars->actionID) {
 case 'rule_down':
 case 'rule_up':
 case 'rule_copy':
@@ -46,7 +42,7 @@ case 'rule_enable':
         header('Location: ' . Horde::applicationUrl('filters.php', true));
         exit;
     }
-    switch ($actionID) {
+    switch ($vars->actionID) {
     case 'rule_delete':
         if (!$delete_allowed) {
             $notification->push(_("You do not have permission to delete filter rules."), 'horde.error');
@@ -54,8 +50,8 @@ case 'rule_enable':
             exit;
         }
 
-        $tmp = $filters->getFilter($id);
-        if ($filters->deleteRule($id)) {
+        $tmp = $filters->getFilter($vars->rulenumber);
+        if ($filters->deleteRule($vars->rulenumber)) {
             $notification->push(sprintf(_("Rule \"%s\" deleted."), $tmp['name']), 'horde.success');
         }
         break;
@@ -79,32 +75,30 @@ case 'rule_enable':
             $notification->push($message, 'horde.error', array('content.raw'));
             break 2;
         } else {
-            $tmp = $filters->getFilter($id);
-            if ($filters->copyRule($id)) {
+            $tmp = $filters->getFilter($vars->rulenumber);
+            if ($filters->copyRule($vars->rulenumber)) {
                 $notification->push(sprintf(_("Rule \"%s\" copied."), $tmp['name']), 'horde.success');
             }
         }
         break;
 
     case 'rule_up':
-        $steps = Horde_Util::getFormData('steps', 1);
-        $filters->ruleUp($id, $steps);
+        $filters->ruleUp($vars->rulenumber, $vars->steps || 1);
         break;
 
     case 'rule_down':
-        $steps = Horde_Util::getFormData('steps', 1);
-        $filters->ruleDown($id, $steps);
+        $filters->ruleDown($vars->rulenumber, $vars->steps || 1);
         break;
 
     case 'rule_disable':
-        $tmp = $filters->getFilter($id);
-        $filters->ruleDisable($id);
+        $tmp = $filters->getFilter($vars->rulenumber);
+        $filters->ruleDisable($vars->rulenumber);
         $notification->push(sprintf(_("Rule \"%s\" disabled."), $tmp['name']), 'horde.success');
         break;
 
     case 'rule_enable':
-        $tmp = $filters->getFilter($id);
-        $filters->ruleEnable($id);
+        $tmp = $filters->getFilter($vars->rulenumber);
+        $filters->ruleEnable($vars->rulenumber);
         $notification->push(sprintf(_("Rule \"%s\" enabled."), $tmp['name']), 'horde.success');
         break;
     }
@@ -122,8 +116,8 @@ case 'settings_save':
         header('Location: ' . Horde::applicationUrl('filters.php', true));
         exit;
     }
-    $prefs->setValue('show_filter_msg', Horde_Util::getFormData('show_filter_msg'));
-    $prefs->setValue('filter_seen', Horde_Util::getFormData('filter_seen'));
+    $prefs->setValue('show_filter_msg', $vars->show_filter_msg);
+    $prefs->setValue('filter_seen', $vars->filter_seen);
     $notification->push(_("Settings successfully updated."), 'horde.success');
     break;
 
@@ -172,7 +166,7 @@ if (count($filter_list) == 0) {
 
         $entry = array();
         $entry['number'] = ++$i;
-        $url = Horde_Util::addParameter($filters_url, 'rulenumber', $rule_number);
+        $url = $filters_url->copy()->add('rulenumber', $rule_number);
         $copyurl = $delurl = $editurl = $name = null;
 
         switch ($filter['action']) {
@@ -207,9 +201,9 @@ if (count($filter_list) == 0) {
             break;
 
         default:
-            $editurl = Horde_Util::addParameter($rule_url, array('edit' => $rule_number, 'actionID' => 'rule_edit'));
-            $delurl  = Horde_Util::addParameter($url, 'actionID', 'rule_delete');
-            $copyurl = Horde_Util::addParameter($url, 'actionID', 'rule_copy');
+            $editurl = $rule_url->copy()->add(array('edit' => $rule_number, 'actionID' => 'rule_edit'));
+            $delurl = $url->copy()->add('actionID', 'rule_delete');
+            $copyurl = $url->copy()->add('actionID', 'rule_copy');
             $entry['filterimg'] = false;
             $name = $filter['name'];
             break;
@@ -274,14 +268,18 @@ if (count($filter_list) == 0) {
         }
 
         /* Create up/down arrow links. */
-        $entry['upurl'] = Horde_Util::addParameter($url, 'actionID', 'rule_up');
-        $entry['downurl'] = Horde_Util::addParameter($url, 'actionID', 'rule_down');
-        $entry['uplink'] = ($i > 1) ? Horde::link($entry['upurl'], _("Move Rule Up")) : false;
-        $entry['downlink'] = ($i < $rule_count) ? Horde::link($entry['downurl'], _("Move Rule Down")) : false;
+        $entry['upurl'] = $url->copy()->add('actionID', 'rule_up');
+        $entry['downurl'] = $url->copy()->add('actionID', 'rule_down');
+        $entry['uplink'] = ($i > 1)
+            ? Horde::link($entry['upurl'], _("Move Rule Up"))
+            : false;
+        $entry['downlink'] = ($i < $rule_count)
+            ? Horde::link($entry['downurl'], _("Move Rule Down"))
+            : false;
 
         if (empty($filter['disable'])) {
             if ($edit_allowed) {
-                $entry['disablelink'] = Horde::link(Horde_Util::addParameter($url, 'actionID', 'rule_disable'), sprintf(_("Disable %s"), $name));
+                $entry['disablelink'] = Horde::link($url->copy()->add('actionID', 'rule_disable'), sprintf(_("Disable %s"), $name));
                 $entry['disableimg'] = Horde::img('enable.png', sprintf(_("Disable %s"), $name));
             } else {
                 $entry['disableimg'] = Horde::img('enable.png');
@@ -291,7 +289,7 @@ if (count($filter_list) == 0) {
             $entry['enableimg'] = false;
         } else {
             if ($edit_allowed) {
-                $entry['enablelink'] = Horde::link(Horde_Util::addParameter($url, 'actionID', 'rule_enable'), sprintf(_("Enable %s"), $name));
+                $entry['enablelink'] = Horde::link($url->copy()->add('actionID', 'rule_enable'), sprintf(_("Enable %s"), $name));
                 $entry['enableimg'] = Horde::img('disable.png', sprintf(_("Enable %s"), $name));
             } else {
                 $entry['enableimg'] = Horde::img('disable.png');
index f742b35..a1470b9 100644 (file)
@@ -21,56 +21,50 @@ if (!in_array(Ingo_Storage::ACTION_FORWARD, $_SESSION['ingo']['script_categories
 }
 
 /* Get the forward object and rule. */
-$forward = &$ingo_storage->retrieve(Ingo_Storage::ACTION_FORWARD);
-$filters = &$ingo_storage->retrieve(Ingo_Storage::ACTION_FILTERS);
+$forward = $ingo_storage->retrieve(Ingo_Storage::ACTION_FORWARD);
+$filters = $ingo_storage->retrieve(Ingo_Storage::ACTION_FILTERS);
 $fwd_id = $filters->findRuleId(Ingo_Storage::ACTION_FORWARD);
 $fwd_rule = $filters->getRule($fwd_id);
 
 /* Load libraries. */
-$vars = &Horde_Variables::getDefaultVariables();
-if ($vars->get('submitbutton') == _("Return to Rules List")) {
+$vars = Horde_Variables::getDefaultVariables();
+if ($vars->submitbutton == _("Return to Rules List")) {
     header('Location: ' . Horde::applicationUrl('filters.php', true));
     exit;
 }
 
 /* Build form. */
 $form = new Horde_Form($vars);
-$v = &$form->addVariable(_("Keep a copy of messages in this account?"), 'keep_copy', 'boolean', false);
+$v = $form->addVariable(_("Keep a copy of messages in this account?"), 'keep_copy', 'boolean', false);
 $v->setHelp('forward-keepcopy');
-$v = &$form->addVariable(_("Address(es) to forward to:"), 'addresses', 'longtext', false, false, null, array(5, 40));
+$v = $form->addVariable(_("Address(es) to forward to:"), 'addresses', 'longtext', false, false, null, array(5, 40));
 $v->setHelp('forward-addresses');
 $form->setButtons(_("Save"));
 
 /* Perform requested actions. */
 if ($form->validate($vars)) {
-    $forward->setForwardAddresses($vars->get('addresses'));
-    $forward->setForwardKeep($vars->get('keep_copy') == 'on');
+    $forward->setForwardAddresses($vars->addresses);
+    $forward->setForwardKeep($vars->keep_copy == 'on');
     $success = true;
-    if (is_a($result = $ingo_storage->store($forward), 'PEAR_Error')) {
-        $notification->push($result);
-        $success = false;
-    } else {
+    try {
+        $ingo_storage->store($forward);
         $notification->push(_("Changes saved."), 'horde.success');
-        if ($vars->get('submitbutton') == _("Save and Enable")) {
+        if ($vars->submitbutton == _("Save and Enable")) {
             $filters->ruleEnable($fwd_id);
-            if (is_a($result = $ingo_storage->store($filters), 'PEAR_Error')) {
-                $notification->push($result);
-                $success = false;
-            } else {
-                $notification->push(_("Rule Enabled"), 'horde.success');
-                $fwd_rule['disable'] = false;
-            }
-        } elseif ($vars->get('submitbutton') == _("Save and Disable")) {
+            $ingo_storage->store($filters);
+            $notification->push(_("Rule Enabled"), 'horde.success');
+            $fwd_rule['disable'] = false;
+        } elseif ($vars->submitbutton == _("Save and Disable")) {
             $filters->ruleDisable($fwd_id);
-            if (is_a($result = $ingo_storage->store($filters), 'PEAR_Error')) {
-                $notification->push($result);
-                $success = false;
-            } else {
-                $notification->push(_("Rule Disabled"), 'horde.success');
-                $fwd_rule['disable'] = true;
-            }
+            $ingo_storage->store($filters);
+            $notification->push(_("Rule Disabled"), 'horde.success');
+            $fwd_rule['disable'] = true;
         }
+    } catch (Ingo_Exception $e) {
+        $notification->push($e);
+        $success = false;
     }
+
     if ($success && $prefs->getValue('auto_update')) {
         Ingo::updateScript();
     }
@@ -86,8 +80,8 @@ $form->appendButtons(_("Return to Rules List"));
 
 /* Set default values. */
 if (!$form->isSubmitted()) {
-    $vars->set('keep_copy', $forward->getForwardKeep());
-    $vars->set('addresses', implode("\n", $forward->getForwardAddresses()));
+    $vars->keep_copy = $forward->getForwardKeep();
+    $vars->addresses = implode("\n", $forward->getForwardAddresses());
 }
 
 /* Set form title. */
index 3cf195d..0bfaecb 100644 (file)
@@ -42,16 +42,16 @@ class Ingo_Api extends Horde_Registry_Api
         }
 
         if (!empty($addresses)) {
-            $blacklist = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_BLACKLIST);
-            $ret = $blacklist->setBlacklist(array_merge($blacklist->getBlacklist(), $addresses));
-            if (is_a($ret, 'PEAR_Error')) {
-                $GLOBALS['notification']->push($ret, $ret->getCode());
-            } else {
+            try {
+                $blacklist = $GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_BLACKLIST);
+                $blacklist->setBlacklist(array_merge($blacklist->getBlacklist(), $addresses));
                 $GLOBALS['ingo_storage']->store($blacklist);
                 Ingo::updateScript();
                 foreach ($addresses as $from) {
                     $GLOBALS['notification']->push(sprintf(_("The address \"%s\" has been added to your blacklist."), $from));
                 }
+            } catch (Ingo_Exception $e) {
+                $GLOBALS['notification']->push($e);
             }
         }
     }
@@ -67,16 +67,16 @@ class Ingo_Api extends Horde_Registry_Api
             $_SESSION['ingo']['current_share'] = $signature;
         }
 
-        $whitelist = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_WHITELIST);
-        $ret = $whitelist->setWhitelist(array_merge($whitelist->getWhitelist(), $addresses));
-        if (is_a($ret, 'PEAR_Error')) {
-            $GLOBALS['notification']->push($ret, $ret->getCode());
-        } else {
+        try {
+            $whitelist = $GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_WHITELIST);
+            $whitelist->setWhitelist(array_merge($whitelist->getWhitelist(), $addresses));
             $GLOBALS['ingo_storage']->store($whitelist);
             Ingo::updateScript();
             foreach ($addresses as $from) {
                 $GLOBALS['notification']->push(sprintf(_("The address \"%s\" has been added to your whitelist."), $from));
             }
+        } catch (Ingo_Exception $e) {
+            $GLOBALS['notification']->push($e);
         }
     }
 
@@ -87,10 +87,11 @@ class Ingo_Api extends Horde_Registry_Api
      */
     public function canApplyFilters()
     {
-        $ingo_script = Ingo::loadIngoScript();
-        return $ingo_script
-            ? $ingo_script->performAvailable()
-            : false;
+        try {
+            return Ingo::loadIngoScript()->performAvailable();
+        } catch (Ingo_Exception $e) {
+            return false;
+        }
     }
 
     /**
@@ -105,16 +106,21 @@ class Ingo_Api extends Horde_Registry_Api
         if (!empty($GLOBALS['ingo_shares'])) {
             $_SESSION['ingo']['current_share'] = $signature;
         }
-        $ingo_script = Ingo::loadIngoScript();
-        if (!$ingo_script) {
+
+        try {
+            $ingo_script = Ingo::loadIngoScript();
+        } catch (Ingo_Exception $e) {
             return false;
         }
+
         if (!isset($params['filter_seen'])) {
             $params['filter_seen'] = $GLOBALS['prefs']->getValue('filter_seen');
         }
+
         if (!isset($params['show_filter_msg'])) {
             $params['show_filter_msg'] = $GLOBALS['prefs']->getValue('show_filter_msg');
         }
+
         return $ingo_script->perform($params);
     }
 
@@ -127,20 +133,20 @@ class Ingo_Api extends Horde_Registry_Api
      */
     public function setVacation($info)
     {
-        if (!empty($GLOBALS['ingo_shares'])) {
-            $_SESSION['ingo']['current_share'] = $signature;
-        }
-
         if (empty($info)) {
             return true;
         }
 
+        if (!empty($GLOBALS['ingo_shares'])) {
+            $_SESSION['ingo']['current_share'] = $signature;
+        }
+
         /* Get vacation filter. */
-        $filters = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FILTERS);
+        $filters = $GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FILTERS);
         $vacation_rule_id = $filters->findRuleId(Ingo_Storage::ACTION_VACATION);
 
         /* Set vacation object and rules. */
-        $vacation = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_VACATION);
+        $vacation = $GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_VACATION);
 
         /* Make sure we have at least one address. */
         if (empty($info['addresses'])) {
@@ -178,17 +184,21 @@ class Ingo_Api extends Horde_Registry_Api
         }
 
         $filters->ruleEnable($vacation_rule_id);
-        $result = $GLOBALS['ingo_storage']->store($filters);
-        if (!is_a($result, 'PEAR_Error')) {
+
+        try {
+            $GLOBALS['ingo_storage']->store($filters);
+
             if ($GLOBALS['prefs']->getValue('auto_update')) {
                 Ingo::updateScript();
             }
 
             /* Update the timestamp for the rules. */
             $_SESSION['ingo']['change'] = time();
-        }
 
-        return $result;
+            return true;
+        } catch (Ingo_Exception $e) {}
+
+        return false;
     }
 
     /**
@@ -203,21 +213,25 @@ class Ingo_Api extends Horde_Registry_Api
         }
 
         /* Get vacation filter. */
-        $filters = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FILTERS);
+        $filters = $GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FILTERS);
         $vacation_rule_id = $filters->findRuleId(Ingo_Storage::ACTION_VACATION);
 
         $filters->ruleDisable($vacation_rule_id);
-        $result = $GLOBALS['ingo_storage']->store($filters);
-        if (!is_a($result, 'PEAR_Error')) {
+
+        try {
+            $GLOBALS['ingo_storage']->store($filters);
+
             if ($GLOBALS['prefs']->getValue('auto_update')) {
                 Ingo::updateScript();
             }
 
             /* Update the timestamp for the rules. */
             $_SESSION['ingo']['change'] = time();
-        }
 
-        return $result;
+            return true;
+        } catch (Ingo_Exception $e) {}
+
+        return false;
     }
 
 }
index b28c3fb..89dd9ed 100644 (file)
@@ -58,10 +58,8 @@ class Ingo_Application extends Horde_Registry_Application
         // Load the Ingo_Storage driver.
         $GLOBALS['ingo_storage'] = Ingo_Storage::factory();
 
-        // Create the ingo session (if needed).
-        if (!isset($_SESSION['ingo']) || !is_array($_SESSION['ingo'])) {
-            Ingo_Session::createSession();
-        }
+        // Create the ingo session.
+        Ingo::createSession();
 
         // Create shares if necessary.
         $driver = Ingo::getDriver();
@@ -159,59 +157,61 @@ class Ingo_Application extends Horde_Registry_Application
      *
      * @param string $user  Name of user to remove data for.
      *
-     * @return mixed  true on success | PEAR_Error on failure
+     * @throws Horde_Auth_Exception.
      */
     public function removeUserData($user)
     {
-        if (!Horde_Auth::isAdmin() && $user != Horde_Auth::getAuth()) {
-            return PEAR::raiseError(_("You are not allowed to remove user data."));
+        if (!Horde_Auth::isAdmin() &&
+            ($user != Horde_Auth::getAuth())) {
+            throw new Horde_Auth_Exception(_("You are not allowed to remove user data."));
         }
 
         /* Remove all filters/rules owned by the user. */
-        $result = $GLOBALS['ingo_storage']->removeUserData($user);
-        if (is_a($result, 'PEAR_Error')) {
-            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
-            return $result;
+        try {
+            $GLOBALS['ingo_storage']->removeUserData($user);
+        } catch (Ingo_Exception $e) {
+            Horde::logMessage($e, __FILE__, __LINE__, PEAR_LOG_ERR);
+            throw new Horde_Auth_Exception($e);
         }
 
         /* Now remove all shares owned by the user. */
         if (!empty($GLOBALS['ingo_shares'])) {
             /* Get the user's default share. */
             $share = $GLOBALS['ingo_shares']->getShare($user);
-            if (is_a($share, 'PEAR_Error')) {
+            if ($share instanceof PEAR_Error) {
                 Horde::logMessage($share, __FILE__, __LINE__, PEAR_LOG_ERR);
-                return $share;
-            } else {
-                $result = $GLOBALS['ingo_shares']->removeShare($share);
-                if (is_a($result, 'PEAR_Error')) {
-                    Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
-                    return $result;
-                }
+                throw new Horde_Auth_Exception($share);
+            }
+
+            $result = $GLOBALS['ingo_shares']->removeShare($share);
+            if ($result instanceof PEAR_Error) {
+                Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+                throw new Horde_Auth_Exception($share);
             }
 
             /* Get a list of all shares this user has perms to and remove the
              * perms. */
             $shares = $GLOBALS['ingo_shares']->listShares($user);
-            if (is_a($shares, 'PEAR_Error')) {
+            if ($shares instanceof PEAR_Error) {
                 Horde::logMessage($shares, __FILE__, __LINE__, PEAR_LOG_ERR);
-            }
-            foreach ($shares as $share) {
-                $share->removeUser($user);
+            } else {
+                foreach ($shares as $share) {
+                    $share->removeUser($user);
+                }
             }
 
             /* Get a list of all shares this user owns and has perms to delete
              * and remove them. */
             $shares = $GLOBALS['ingo_shares']->listShares($user, Horde_Perms::DELETE, $user);
-            if (is_a($shares, 'PEAR_Error')) {
+            if ($shares instanceof PEAR_Error) {
                 Horde::logMessage($shares, __FILE__, __LINE__, PEAR_LOG_ERR);
-                return $shares;
+                throw new Horde_Auth_Exception($share);
             }
+
             foreach ($shares as $share) {
                 $GLOBALS['ingo_shares']->removeShare($share);
             }
         }
-
-        return true;
     }
 
 }
index b85d5e2..96482c0 100644 (file)
@@ -28,24 +28,24 @@ class Ingo_Driver
     protected $_support_shares = false;
 
     /**
-     * Attempts to return a concrete Ingo_Driver instance based on $driver.
+     * Attempts to return a concrete instance based on $driver.
      *
-     * @param string $driver  The type of concrete Ingo_Driver subclass to
-     *                        return.
-     * @param array $params   A hash containing any additional configuration or
-     *                        connection parameters a subclass might need.
+     * @param string $driver  The type of concrete subclass to return.
+     * @param array $params   A hash containing any additional configuration
+     *                        or connection parameters a subclass might need.
      *
-     * @return mixed  The newly created concrete Ingo_Driver instance, or
-     *                false on error.
+     * @return Ingo_Driver  The newly created concrete instance.
+     * @throws Ingo_Exception
      */
     static public function factory($driver, $params = array())
     {
-        $driver = basename($driver);
-        $class = 'Ingo_Driver_' . ucfirst($driver);
+        $class = __CLASS__ . '_' . ucfirst(basename($driver));
 
-        return class_exists($class)
-            ? new $class($params)
-            : false;
+        if (class_exists($class)) {
+            return new $class($params);
+        }
+
+        throw new Ingo_Exception('Could not load driver.');
     }
 
     /**
@@ -61,8 +61,8 @@ class Ingo_Driver
      *
      * @param string $script  The filter script.
      *
-     * @return mixed  True on success, false if script can't be activated.
-     *                Returns PEAR_Error on error.
+     * @return boolean  True on success, false if script can't be activated.
+     * @throws Ingo_Exception
      */
     public function setScriptActive($script)
     {
@@ -76,7 +76,8 @@ class Ingo_Driver
      */
     public function supportShares()
     {
-        return $this->_support_shares && !empty($_SESSION['ingo']['backend']['shares']);
+        return ($this->_support_shares &&
+                !empty($_SESSION['ingo']['backend']['shares']));
     }
 
 }
index 5986d9c..f4ec454 100644 (file)
@@ -14,12 +14,12 @@ class Ingo_Driver_Ldap extends Ingo_Driver
     /**
      * Constructor.
      *
-     * @throws Horde_Exception
+     * @throws Ingo_Exception
      */
     public function __construct($params = array())
     {
         if (!Horde_Util::extensionExists('ldap')) {
-            throw new Horde_Exception(_("LDAP support is required but the LDAP module is not available or not loaded."));
+            throw new Ingo_Exception(_("LDAP support is required but the LDAP module is not available or not loaded."));
         }
 
         $default_params = array(
@@ -64,12 +64,14 @@ class Ingo_Driver_Ldap extends Ingo_Driver
 
     /**
      * Connect and bind to ldap server.
+     *
+     * @throws Ingo_Exception
      */
     protected function _connect()
     {
         if (!($ldapcn = @ldap_connect($this->_params['hostspec'],
                                       $this->_params['port']))) {
-            return PEAR::raiseError(_("Connection failure"));
+            throw new Ingo_Exception(_("Connection failure"));
         }
 
         /* Set the LDAP protocol version. */
@@ -80,39 +82,34 @@ class Ingo_Driver_Ldap extends Ingo_Driver
         }
 
         /* Start TLS if we're using it. */
-        if (!empty($this->_params['tls'])) {
-            if (!@ldap_start_tls($ldapcn)) {
-                return PEAR::raiseError(sprintf(_("STARTTLS failed: (%s) %s"),
-                                                ldap_errno($ldapcn),
-                                                ldap_error($ldapcn)));
-            }
+        if (!empty($this->_params['tls']) &&
+            !@ldap_start_tls($ldapcn)) {
+            throw new Ingo_Exception(sprintf(_("STARTTLS failed: (%s) %s"),
+                                     ldap_errno($ldapcn),
+                                     ldap_error($ldapcn)));
         }
 
         /* Bind to the server. */
         if (isset($this->_params['bind_dn'])) {
             $bind_dn = $this->_substUser($this->_params['bind_dn']);
-            if (is_a($bind_dn, 'PEAR_Error')) {
-                return $bind_dn;
-            }
 
-            if (isset($this->_params['bind_password'])) {
-                $password = $this->_params['bind_password'];
-            } else {
-                $password = $this->_params['password'];
-            }
+            $password = isset($this->_params['bind_password'])
+                ? $this->_params['bind_password']
+                : $this->_params['password'];
 
-            if (!@ldap_bind($ldapcn, $bind_dn, $password)) {
-                return PEAR::raiseError(sprintf(_("Bind failed: (%s) %s"),
-                                                ldap_errno($ldapcn),
-                                                ldap_error($ldapcn)));
-            }
-        } elseif (!(@ldap_bind($ldapcn))) {
-            return PEAR::raiseError(sprintf(_("Bind failed: (%s) %s"),
-                                            ldap_errno($ldapcn),
-                                            ldap_error($ldapcn)));
+            $bind_success = @ldap_bind($ldapcn, $bind_dn, $password);
+        } else {
+            $bind_success = @ldap_bind($ldapcn);
         }
 
-        return $ldapcn;
+        if ($bind_success) {
+            return $ldapcn;
+        }
+
+
+        throw new Ingo_Exception(sprintf(_("Bind failed: (%s) %s"),
+                                 ldap_errno($ldapcn),
+                                 ldap_error($ldapcn)));
     }
 
     /**
@@ -121,7 +118,8 @@ class Ingo_Driver_Ldap extends Ingo_Driver
      * @param resource $ldapcn  The connection to the LDAP server.
      * @param string $userDN    Set to the user object's real DN.
      *
-     * @return mixed  Array of script sources, or PEAR_Error on failure.
+     * @return array  Script sources list.
+     * @throws Ingo_Exception
      */
     protected function _getScripts($ldapcn, &$userDN)
     {
@@ -132,28 +130,30 @@ class Ingo_Driver_Ldap extends Ingo_Driver
         $sr = @ldap_search($ldapcn, $this->_params['script_base'], $filter,
                            $attrs);
         if ($sr === false) {
-            return PEAR::raiseError(sprintf(_("Error retrieving current script: (%d) %s"),
-                                            ldap_errno($ldapcn),
-                                            ldap_error($ldapcn)));
+            throw new Ingo_Exception(sprintf(_("Error retrieving current script: (%d) %s"),
+                                     ldap_errno($ldapcn),
+                                     ldap_error($ldapcn)));
         }
+
         if (@ldap_count_entries($ldapcn, $sr) != 1) {
-            return PEAR::raiseError(sprintf(_("Expected 1 object, got %d."),
-                                            ldap_count_entries($ldapcn, $sr)));
+            throw new Ingo_Exception(sprintf(_("Expected 1 object, got %d."),
+                                     ldap_count_entries($ldapcn, $sr)));
         }
+
         $ent = @ldap_first_entry($ldapcn, $sr);
         if ($ent === false) {
-            return PEAR::raiseError(sprintf(_("Error retrieving current script: (%d) %s"),
-                                            ldap_errno($ldapcn),
-                                            ldap_error($ldapcn)));
+            throw new Ingo_Exception(sprintf(_("Error retrieving current script: (%d) %s"),
+                                     ldap_errno($ldapcn),
+                                     ldap_error($ldapcn)));
         }
 
         /* Retrieve the user's DN. */
         $v = @ldap_get_dn($ldapcn, $ent);
         if ($v === false) {
             @ldap_free_result($sr);
-            return PEAR::raiseError(sprintf(_("Error retrieving current script: (%d) %s"),
-                                            ldap_errno($ldapcn),
-                                            ldap_error($ldapcn)));
+            throw new Ingo_Exception(sprintf(_("Error retrieving current script: (%d) %s"),
+                                     ldap_errno($ldapcn),
+                                     ldap_error($ldapcn)));
         }
         $userDN = $v;
 
@@ -161,9 +161,9 @@ class Ingo_Driver_Ldap extends Ingo_Driver
         $attrs = @ldap_get_attributes($ldapcn, $ent);
         @ldap_free_result($sr);
         if ($attrs === false) {
-            return PEAR::raiseError(sprintf(_("Error retrieving current script: (%d) %s"),
-                                            ldap_errno($ldapcn),
-                                            ldap_error($ldapcn)));
+            throw new Ingo_Exception(sprintf(_("Error retrieving current script: (%d) %s"),
+                                     ldap_errno($ldapcn),
+                                     ldap_error($ldapcn)));
         }
 
         /* Attribute can be in any case, and can have a ";binary"
@@ -186,19 +186,12 @@ class Ingo_Driver_Ldap extends Ingo_Driver
      *
      * @param string $script  The sieve script.
      *
-     * @return mixed  True on success, PEAR_Error on error.
+     * @throws Ingo_Exception
      */
     protected function setScriptActive($script)
     {
         $ldapcn = $this->_connect();
-        if (is_a($ldapcn, 'PEAR_Error')) {
-            return $ldapcn;
-        }
-
         $values = $this->_getScripts($ldapcn, $userDN);
-        if (is_a($values, 'PEAR_Error')) {
-            return $values;
-        }
 
         $found = false;
         foreach ($values as $i => $value) {
@@ -212,24 +205,25 @@ class Ingo_Driver_Ldap extends Ingo_Driver
                 break;
             }
         }
+
         if (!$found && !empty($script)) {
             $values[] = $script;
         }
 
         $replace = array(Horde_String::lower($this->_params['script_attribute']) => $values);
-        if (empty($values)) {
-            $r = @ldap_mod_del($ldapcn, $userDN, $replace);
-        } else {
-            $r = @ldap_mod_replace($ldapcn, $userDN, $replace);
-        }
+        $r = empty($values)
+            ? @ldap_mod_del($ldapcn, $userDN, $replace)
+            : @ldap_mod_replace($ldapcn, $userDN, $replace);
+
         if (!$r) {
-            return PEAR::raiseError(sprintf(_("Activating the script for \"%s\" failed: (%d) %s"),
-                                            $userDN,
-                                            ldap_errno($ldapcn),
-                                            ldap_error($ldapcn)));
+            throw new Ingo_Exception(sprintf(_("Activating the script for \"%s\" failed: (%d) %s"),
+                                     $userDN,
+                                     ldap_errno($ldapcn),
+                                     ldap_error($ldapcn)));
         }
 
         @ldap_close($ldapcn);
+
         return true;
     }
 
@@ -237,18 +231,13 @@ class Ingo_Driver_Ldap extends Ingo_Driver
      * Returns the content of the currently active script.
      *
      * @return string  The complete ruleset of the specified user.
+     *
+     * @throws Ingo_Exception
      */
     public function getScript()
     {
         $ldapcn = $this->_connect();
-        if (is_a($ldapcn, 'PEAR_Error')) {
-            return $ldapcn;
-        }
-
         $values = $this->_getScripts($ldapcn, $userDN);
-        if (is_a($values, 'PEAR_Error')) {
-            return $values;
-        }
 
         $script = '';
         foreach ($values as $value) {
index 8cc36cf..fda72d8 100644 (file)
@@ -45,12 +45,12 @@ class Ingo_Driver_Sivtest extends Ingo_Driver
     /**
      * Connect to the sieve server.
      *
-     * @return mixed  True on success, PEAR_Error on false.
+     * @throws Ingo_Exception;
      */
     protected function _connect()
     {
         if (!empty($this->_sieve)) {
-            return true;
+            return;
         }
 
         $this->sivtestSocket($this->_params['username'],
@@ -68,11 +68,9 @@ class Ingo_Driver_Sivtest extends Ingo_Driver
                                       $this->_params['usetls']);
 
         $res = $this->_sieve->getError();
-        if (is_a($res, 'PEAR_Error')) {
+        if ($res instanceof PEAR_Error) {
             unset($this->_sieve);
-            return $res;
-        } else {
-            return true;
+            throw new Ingo_Exception($res);
         }
     }
 
@@ -81,19 +79,15 @@ class Ingo_Driver_Sivtest extends Ingo_Driver
      *
      * @param string $script  The sieve script.
      *
-     * @return mixed  True on success.
-     *                Returns PEAR_Error on error.
+     * @throws Ingo_Exception
      */
     public function setScriptActive($script)
     {
-        $res = $this->_connect();
-        if (is_a($res, 'PEAR_Error')) {
-            return $res;
-        }
+        $this->_connect();
 
         $res = $this->_sieve->haveSpace($this->_params['scriptname'], strlen($script));
-        if (is_a($res, 'PEAR_ERROR')) {
-            return $res;
+        if ($res instanceof PEAR_Error) {
+            throw new Ingo_Exception($res);
         }
 
         return $this->_sieve->installScript($this->_params['scriptname'], $script, true);
@@ -103,13 +97,11 @@ class Ingo_Driver_Sivtest extends Ingo_Driver
      * Returns the content of the currently active script.
      *
      * @return string  The complete ruleset of the specified user.
+     * @throws Ingo Exception
      */
     public function getScript()
     {
-        $res = $this->_connect();
-        if (is_a($res, 'PEAR_Error')) {
-            return $res;
-        }
+        $this->_connect();
         return $this->_sieve->getScript($this->_sieve->getActive());
     }
 
@@ -122,6 +114,7 @@ class Ingo_Driver_Sivtest extends Ingo_Driver
      * @param string $hostspec  The hostspec.
      *
      * @return TODO
+     * @throws Ingo_Exception
      */
     public function sivtestSocket($username, $password, $hostspec)
     {
@@ -160,7 +153,7 @@ class Ingo_Driver_Sivtest extends Ingo_Driver
             }
             $socket = new Net_Socket();
             $error = $socket->connect($domain_socket, 0, true, 30);
-            if (!is_a($error, 'PEAR_Error')) {
+            if (!($error instanceof PEAR_Error)) {
                 break;
             }
 
@@ -169,22 +162,22 @@ class Ingo_Driver_Sivtest extends Ingo_Driver
         }
 
         if (!empty($error_return)) {
-            return PEAR::raiseError(_($error_return));
+            throw new Ingo_Exception($error_return);
         }
 
         $status = $socket->getStatus();
-        if (is_a($status, 'PEAR_Error') || $status['eof']) {
-            return PEAR::raiseError(_('Failed to write to socket: (connection lost!)'));
+        if ($status instanceof PEAR_Error || $status['eof']) {
+            throw new Ingo_Exception(_("Failed to write to socket: (connection lost!)"));
         }
 
         $error = $socket->writeLine("CAPABILITY");
-        if (is_a($error, 'PEAR_Error')) {
-            return PEAR::raiseError(_('Failed to write to socket: ' . $error->getMessage()));
+        if ($error instanceof PEAR_Error) {
+            throw new Ingo_Exception(_("Failed to write to socket: " . $error->getMessage()));
         }
 
         $result = $socket->readLine();
-        if (is_a($result, 'PEAR_Error')) {
-            return PEAR::raiseError(_('Failed to read from socket: ' . $error->getMessage()));
+        if ($result instanceof PEAR_Error) {
+            throw new Ingo_Exception(_("Failed to read from socket: " . $error->getMessage()));
         }
 
         if (preg_match('|^bye \(referral "(sieve://)?([^"]+)|i',
index 92867b8..e7c3af4 100644 (file)
@@ -42,6 +42,8 @@ class Ingo_Driver_Timsieved extends Ingo_Driver
 
     /**
      * Connects to the sieve server.
+     *
+     * @throws Ingo_Exception
      */
     protected function _connect()
     {
@@ -49,11 +51,10 @@ class Ingo_Driver_Timsieved extends Ingo_Driver
             return;
         }
 
-        if (empty($this->_params['admin'])) {
-            $auth = $this->_params['username'];
-        } else {
-            $auth = $this->_params['admin'];
-        }
+        $auth = empty($this->_params['admin'])
+            ? $this->_params['username']
+            : $this->_params['admin'];
+
         $this->_sieve = new Net_Sieve($auth,
                                       $this->_params['password'],
                                       $this->_params['hostspec'],
@@ -65,9 +66,9 @@ class Ingo_Driver_Timsieved extends Ingo_Driver
                                       $this->_params['usetls']);
 
         $res = $this->_sieve->getError();
-        if (is_a($res, 'PEAR_Error')) {
+        if ($res instanceof PEAR_Error) {
             unset($this->_sieve);
-            return $res;
+            throw new Ingo_Exception($res);
         }
 
         if (!empty($this->_params['debug'])) {
@@ -91,23 +92,20 @@ class Ingo_Driver_Timsieved extends Ingo_Driver
      *
      * @param string $script  The sieve script.
      *
-     * @return mixed  True on success, PEAR_Error on error.
+     * @return mixed  True on success.
+     * @throws Ingo_Exception
      */
     public function setScriptActive($script)
     {
         $res = $this->_connect();
-        if (is_a($res, 'PEAR_Error')) {
-            return $res;
-        }
 
         if (!strlen($script)) {
             return $this->_sieve->setActive('');
         }
 
-        $res = $this->_sieve->haveSpace($this->_params['scriptname'],
-                                        strlen($script));
-        if (is_a($res, 'PEAR_ERROR')) {
-            return $res;
+        $res = $this->_sieve->haveSpace($this->_params['scriptname'], strlen($script));
+        if ($res instanceof PEAR_Error) {
+            throw new Ingo_Exception($res);
         }
 
         return $this->_sieve->installScript($this->_params['scriptname'],
@@ -118,18 +116,16 @@ class Ingo_Driver_Timsieved extends Ingo_Driver
      * Returns the content of the currently active script.
      *
      * @return string  The complete ruleset of the specified user.
+     * @throws Ingo_Exception
      */
     public function getScript()
     {
         $res = $this->_connect();
-        if (is_a($res, 'PEAR_Error')) {
-            return $res;
-        }
         $active = $this->_sieve->getActive();
-        if (empty($active)) {
-            return '';
-        }
-        return $this->_sieve->getScript($active);
+
+        return empty($active)
+            ? ''
+            : $this->_sieve->getScript($active);
     }
 
 }
index 262ef69..83bc49d 100644 (file)
@@ -37,50 +37,46 @@ class Ingo_Driver_Vfs extends Ingo_Driver
     /**
      * Sets a script running on the backend.
      *
-     * @param string $script  The filter script
+     * @param string $script  The filter script.
      *
-     * @return mixed  True on success, or PEAR_Error on failure.
+     * @return mixed  True on success.
+     * @throws Ingo_Exception
      */
     public function setScriptActive($script)
     {
-        $result = $this->_connect();
-        if (is_a($result, 'PEAR_Error')) {
-            return $result;
-        }
+        $this->_connect();
 
-        if (empty($script)) {
-            $result = $this->_vfs->deleteFile($this->_params['vfs_path'], $this->_params['filename']);
-        } else {
-            $result = $this->_vfs->writeData($this->_params['vfs_path'], $this->_params['filename'], $script, true);
-        }
-        if (is_a($result, 'PEAR_Error')) {
-            return $result;
+        $result = empty($script)
+            ? $this->_vfs->deleteFile($this->_params['vfs_path'], $this->_params['filename'])
+            : $this->_vfs->writeData($this->_params['vfs_path'], $this->_params['filename'], $script, true);
+        if ($result instanceof PEAR_Error) {
+            throw new Ingo_Exception($result);
         }
 
         if (isset($this->_params['file_perms']) && !empty($script)) {
             $result = $this->_vfs->changePermissions($this->_params['vfs_path'], $this->_params['filename'], $this->_params['file_perms']);
-            if (is_a($result, 'PEAR_Error')) {
-                return $result;
+            if ($result instanceof PEAR_Error) {
+                throw new Ingo_Exception($result);
             }
         }
 
         // Get the backend; necessary if a .forward is needed for
         // procmail.
         $backend = Ingo::getBackend();
-        if ($backend['script'] == 'procmail' && isset($backend['params']['forward_file']) && isset($backend['params']['forward_string'])) {
-            if (empty($script)) {
-                $result = $this->_vfs->deleteFile($this->_params['vfs_forward_path'], $backend['params']['forward_file']);
-            } else {
-                $result = $this->_vfs->writeData($this->_params['vfs_forward_path'], $backend['params']['forward_file'], $backend['params']['forward_string'], true);
-            }
-            if (is_a($result, 'PEAR_Error')) {
-                return $result;
+        if (($backend['script'] == 'procmail') &&
+            isset($backend['params']['forward_file']) &&
+            isset($backend['params']['forward_string'])) {
+            $result = empty($script)
+                ? $this->_vfs->deleteFile($this->_params['vfs_forward_path'], $backend['params']['forward_file'])
+                : $this->_vfs->writeData($this->_params['vfs_forward_path'], $backend['params']['forward_file'], $backend['params']['forward_string'], true);
+            if ($result instanceof PEAR_Error) {
+                throw new Ingo_Exception($result);
             }
 
             if (isset($this->_params['file_perms']) && !empty($script)) {
                 $result = $this->_vfs->changePermissions($this->_params['vfs_forward_path'], $backend['params']['forward_file'], $this->_params['file_perms']);
-                if (is_a($result, 'PEAR_Error')) {
-                    return $result;
+                if ($result instanceof PEAR_Error) {
+                    throw new Ingo_Exception($result);
                 }
             }
         }
@@ -92,20 +88,18 @@ class Ingo_Driver_Vfs extends Ingo_Driver
      * Returns the content of the currently active script.
      *
      * @return string  The complete ruleset of the specified user.
+     * @throws Ingo_Exception
      */
     public function getScript()
     {
-        $result = $this->_connect();
-        if (is_a($result, 'PEAR_Error')) {
-            return $result;
-        }
+        $this->_connect();
         return $this->_vfs->read($this->_params['vfs_path'], $this->_params['filename']);
     }
 
     /**
      * Connect to the VFS server.
      *
-     * @return boolean  True on success, PEAR_Error on false.
+     * @throws Ingo_Exception
      */
     protected function _connect()
     {
@@ -130,13 +124,11 @@ class Ingo_Driver_Vfs extends Ingo_Driver
             return true;
         }
 
-        $this->_vfs = &VFS::singleton($this->_params['vfstype'], $this->_params);
-        if (is_a($this->_vfs, 'PEAR_Error')) {
-            $error = $this->_vfs;
-            $this->_vfs = null;
-            return $error;
-        } else {
-            return true;
+        $this->_vfs = VFS::singleton($this->_params['vfstype'], $this->_params);
+        if ($this->_vfs instanceof PEAR_Error) {
+            $error = new Ingo_Exception($this->_vfs);
+            unset($this->_vfs);
+            throw new Ingo_Exception($error);
         }
     }
 
diff --git a/ingo/lib/Exception.php b/ingo/lib/Exception.php
new file mode 100644 (file)
index 0000000..a8df59f
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+/**
+ * Base exception class for Ingo.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Michael Slusarz <slusarz@horde.org>
+ * @package Ingo
+ */
+class Ingo_Exception extends Horde_Exception
+{
+}
index cc8c6cd..40588f4 100644 (file)
@@ -22,13 +22,75 @@ class Ingo
      */
     const USER_HEADER = '++USER_HEADER++';
 
-    /* getMenu() cache. */
+    /**
+     * getMenu() cache.
+     *
+     * @var string
+     */
     static private $_menuCache = null;
 
-    /* hasSharePermission() cache. */
+    /**
+     * hasSharePermission() cache.
+     *
+     * @var integer
+     */
     static private $_shareCache = null;
 
     /**
+     * Create an ingo session.
+     *
+     * Creates the $ingo session variable with the following entries:
+     * 'backend' (array) - The backend configuration to use.
+     * 'change' (integer) - The timestamp of the last time the rules were
+     *                      altered.
+     * 'storage' (array) - Used by Ingo_Storage:: for caching data.
+     * 'script_categories' (array) - The list of available categories for the
+     *                               Ingo_Script driver in use.
+     * 'script_generate' (boolean) - Is the Ingo_Script::generate() call
+     *                               available?
+     *
+     * @throws Ingo_Exception
+     */
+    static public function createSession()
+    {
+        if (isset($_SESSION['ingo'])) {
+            return;
+        }
+
+        global $prefs;
+
+        $_SESSION['ingo'] = array(
+            'backend' => Ingo::getBackend(),
+            'change' => 0,
+            'storage' => array()
+        );
+
+        $ingo_script = Ingo::loadIngoScript();
+        $_SESSION['ingo']['script_generate'] = $ingo_script->generateAvailable();
+
+        /* Disable categories as specified in preferences */
+        $disabled = array();
+        if ($prefs->isLocked('blacklist')) {
+            $disabled[] = Ingo_Storage::ACTION_BLACKLIST;
+        }
+        if ($prefs->isLocked('whitelist')) {
+            $disabled[] = Ingo_Storage::ACTION_WHITELIST;
+        }
+        if ($prefs->isLocked('vacation')) {
+            $disabled[] = Ingo_Storage::ACTION_VACATION;
+        }
+        if ($prefs->isLocked('forward')) {
+            $disabled[] = Ingo_Storage::ACTION_FORWARD;
+        }
+        if ($prefs->isLocked('spam')) {
+            $disabled[] = Ingo_Storage::ACTION_SPAM;
+        }
+
+        /* Set the list of categories this driver supports. */
+        $_SESSION['ingo']['script_categories'] = array_merge($ingo_script->availableActions(), array_diff($ingo_script->availableCategories(), $disabled));
+    }
+
+    /**
      * Generates a folder widget.
      * If an application is available that provides a folderlist method
      * then a &lt;select&gt; input is created else a simple text field
@@ -153,25 +215,28 @@ class Ingo
      */
     static public function activateScript($script, $deactivate = false)
     {
-        global $notification;
-
         $driver = self::getDriver();
-        $res = $driver->setScriptActive($script);
-        if (is_a($res, 'PEAR_Error')) {
+
+        try {
+            $res = $driver->setScriptActive($script);
+        } catch (Ingo_Exception $e) {
             $msg = ($deactivate)
               ? _("There was an error deactivating the script.")
               : _("There was an error activating the script.");
-            $notification->push($msg . ' ' . _("The driver said: ") . $res->getMessage(), 'horde.error');
+            $GLOBALS['notification']->push($msg . ' ' . _("The driver said: ") . $e->getMessage(), 'horde.error');
+            return false;
+        }
+
+        if ($res === false) {
             return false;
-        } elseif ($res === true) {
-            $msg = ($deactivate)
-              ? _("Script successfully deactivated.")
-              : _("Script successfully activated.");
-            $notification->push($msg, 'horde.success');
-            return true;
         }
 
-        return false;
+        $msg = ($deactivate)
+            ? _("Script successfully deactivated.")
+            : _("Script successfully activated.");
+        $GLOBALS['notification']->push($msg, 'horde.success');
+
+        return true;
     }
 
     /**
@@ -181,8 +246,7 @@ class Ingo
      */
     static public function getScript()
     {
-        $driver = self::getDriver();
-        return $driver->getScript();
+        return self::getDriver()->getScript();
     }
 
     /**
@@ -190,16 +254,15 @@ class Ingo
      */
     static public function updateScript()
     {
-        global $notification;
-
         if ($_SESSION['ingo']['script_generate']) {
-            $ingo_script = self::loadIngoScript();
-            if (!$ingo_script) {
-                $notification->push(_("Script not updated."), 'horde.error');
-            } else {
+            try {
+                $ingo_script = self::loadIngoScript();
+
                 /* Generate and activate the script. */
                 $script = $ingo_script->generate();
                 self::activateScript($script);
+            } catch (Ingo_Exception $e) {
+                $GLOBALS['notification']->push(_("Script not updated."), 'horde.error');
             }
         }
     }
@@ -213,7 +276,7 @@ class Ingo
      * single value or an array of multiple values.
      *
      * @return array  The backend entry.
-     * @throws Horde_Exception
+     * @throws Ingo_Exception
      */
     static public function getBackend()
     {
@@ -243,16 +306,16 @@ class Ingo
 
         /* Check for valid backend configuration. */
         if (!isset($backend)) {
-            throw new Horde_Exception(_("No backend configured for this host"));
+            throw new Ingo_Exception(_("No backend configured for this host"));
         }
 
         $backends[$backend]['id'] = $name;
         $backend = $backends[$backend];
 
         if (empty($backend['script'])) {
-            throw new Horde_Exception(sprintf(_("No \"%s\" element found in backend configuration."), 'script'));
+            throw new Ingo_Exception(sprintf(_("No \"%s\" element found in backend configuration."), 'script'));
         } elseif (empty($backend['driver'])) {
-            throw new Horde_Exception(sprintf(_("No \"%s\" element found in backend configuration."), 'driver'));
+            throw new Ingo_Exception(sprintf(_("No \"%s\" element found in backend configuration."), 'driver'));
         }
 
         /* Make sure the 'params' entry exists. */
@@ -267,23 +330,19 @@ class Ingo
      * Loads a Ingo_Script:: backend and checks for errors.
      *
      * @return Ingo_Script  Script object on success.
-     * @throws Horde_Exception
+     * @throws Ingo_Exception
      */
     static public function loadIngoScript()
     {
-        $ingo_script = Ingo_Script::factory($_SESSION['ingo']['backend']['script'],
-                                            isset($_SESSION['ingo']['backend']['scriptparams']) ? $_SESSION['ingo']['backend']['scriptparams'] : array());
-        if (is_a($ingo_script, 'PEAR_Error')) {
-            throw new Horde_Exception($ingo_script);
-        }
-
-        return $ingo_script;
+        return Ingo_Script::factory($_SESSION['ingo']['backend']['script'],
+                                    isset($_SESSION['ingo']['backend']['scriptparams']) ? $_SESSION['ingo']['backend']['scriptparams'] : array());
     }
 
     /**
      * Returns an instance of the configured driver.
      *
      * @return Ingo_Driver  The configured driver.
+     * @throws Ingo_Exception
      */
     static public function getDriver()
     {
@@ -320,7 +379,7 @@ class Ingo
                                         $permission = Horde_Perms::SHOW)
     {
         $rulesets = $GLOBALS['ingo_shares']->listShares(Horde_Auth::getAuth(), $permission, $owneronly ? Horde_Auth::getAuth() : null);
-        if (is_a($rulesets, 'PEAR_Error')) {
+        if ($rulesets instanceof PEAR_Error) {
             Horde::logMessage($rulesets, __FILE__, __LINE__, PEAR_LOG_ERR);
             return array();
         }
index d0b01ee..b64b2c1 100644 (file)
@@ -103,19 +103,19 @@ class Ingo_Script
     protected $_scriptfile = false;
 
     /**
-     * Attempts to return a concrete Ingo_Script instance based on $script.
+     * Attempts to return a concrete instance based on $script.
      *
-     * @param string $script  The type of Ingo_Script subclass to return.
-     * @param array $params   Hash containing additional paramters to be passed
-     *                        to the subclass' constructor.
+     * @param string $script  The type of subclass to return.
+     * @param array $params   Hash containing additional paramters to be
+     *                        passed to the subclass' constructor.
      *
-     * @return Ingo_Script  The newly created concrete Ingo_Script instance, or
-     *                      false on error.
+     * @return Ingo_Script  The newly created concrete instance.
+     * @throws Ingo_Exception
      */
     static public function factory($script, $params = array())
     {
         $script = Horde_String::ucfirst(basename($script));
-        $class = 'Ingo_Script_' . $script;
+        $class = __CLASS__ . '_' . $script;
 
         if (!isset($params['spam_compare'])) {
             $params['spam_compare'] = $GLOBALS['conf']['spam']['compare'];
@@ -140,9 +140,11 @@ class Ingo_Script
             }
         }
 
-        return class_exists($class)
-            ? new $class($params)
-            : PEAR::raiseError(sprintf(_("Unable to load the definition of %s."), $class));
+        if (class_exists($class)) {
+            return new $class($params);
+        }
+
+        throw new Ingo_Exception(sprintf(_("Unable to load the definition of %s."), $class));
     }
 
     /**
@@ -154,10 +156,6 @@ class Ingo_Script
     {
         $this->_params = $params;
 
-        if (!isset($GLOBALS['registry'])) {
-            return;
-        }
-
         /* Determine if ingo should handle the blacklist. */
         $key = array_search(Ingo_Storage::ACTION_BLACKLIST, $this->_categories);
         if ($key !== false && ($GLOBALS['registry']->hasMethod('mail/blacklistFrom') != 'ingo')) {
@@ -268,8 +266,6 @@ class Ingo_Script
     /**
      * Returns a script previously generated with generate().
      *
-     * @abstract
-     *
      * @return string  The script.
      */
     public function toCode()
@@ -291,8 +287,6 @@ class Ingo_Script
      * Generates the script to do the filtering specified in
      * the rules.
      *
-     * @abstract
-     *
      * @return string  The script.
      */
     public function generate()
@@ -313,8 +307,6 @@ class Ingo_Script
     /**
      * Perform the filtering specified in the rules.
      *
-     * @abstract
-     *
      * @param array $params  The parameter array.
      *
      * @return boolean  True if filtering performed, false if not.
@@ -340,8 +332,6 @@ class Ingo_Script
      * function to be called from within Ingo ensuring that all necessary
      * parameters are set.
      *
-     * @abstract
-     *
      * @return boolean  See perform().
      */
     public function apply()
index e0e8e4a..a8a1425 100644 (file)
@@ -116,7 +116,7 @@ class Ingo_Script_Imap extends Ingo_Script
         }
 
         /* Grab the rules list. */
-        $filters = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FILTERS);
+        $filters = $GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FILTERS);
 
         /* Parse through the rules, one-by-one. */
         foreach ($filters->getFilterList() as $rule) {
index 91362f8..3f3909c 100644 (file)
  * @author  Matt Weyland <mathias@weyland.ch>
  * @package Ingo
  */
+class Ingo_Script_Maildrop extends Ingo_Script
+{
 
-/**
- * Additional storage action since maildrop does not support the
- * "c-flag" as in procmail.
- */
-define('MAILDROP_STORAGE_ACTION_STOREANDFORWARD', 100);
-
-/**
- */
-class Ingo_Script_Maildrop extends Ingo_Script {
+    /* Additional storage action since maildrop does not support the
+     * "c-flag" as in procmail. */
+    const MAILDROP_STORAGE_ACTION_STOREANDFORWARD = 100;
 
     /**
      * The list of actions allowed (implemented) for this driver.
      *
      * @var array
      */
-    var $_actions = array(
+    protected $_actions = array(
         Ingo_Storage::ACTION_KEEP,
         Ingo_Storage::ACTION_MOVE,
         Ingo_Storage::ACTION_DISCARD,
@@ -40,7 +36,7 @@ class Ingo_Script_Maildrop extends Ingo_Script {
      *
      * @var array
      */
-    var $_categories = array(
+    protected $_categories = array(
         Ingo_Storage::ACTION_BLACKLIST,
         Ingo_Storage::ACTION_WHITELIST,
         Ingo_Storage::ACTION_VACATION,
@@ -53,7 +49,7 @@ class Ingo_Script_Maildrop extends Ingo_Script {
      *
      * @var array
      */
-    var $_types = array(
+    protected $_types = array(
         Ingo_Storage::TYPE_HEADER,
     );
 
@@ -62,7 +58,7 @@ class Ingo_Script_Maildrop extends Ingo_Script {
      *
      * @var array
      */
-    var $_tests = array(
+    protected $_tests = array(
         'contains', 'not contain',
         'is', 'not is',
         'begins with','not begins with',
@@ -80,35 +76,35 @@ class Ingo_Script_Maildrop extends Ingo_Script {
      *
      * @var boolean
      */
-    var $_casesensitive = true;
+    protected $_casesensitive = true;
 
     /**
      * Does the driver support the stop-script option?
      *
      * @var boolean
      */
-    var $_supportStopScript = false;
+    protected $_supportStopScript = false;
 
     /**
      * Does the driver require a script file to be generated?
      *
      * @var boolean
      */
-    var $_scriptfile = true;
+    protected $_scriptfile = true;
 
     /**
      * The recipes that make up the code.
      *
      * @var array
      */
-    var $_recipes = array();
+    protected $_recipes = array();
 
     /**
      * Returns a script previously generated with generate().
      *
      * @return string  The maildrop script.
      */
-    function toCode()
+    public function toCode()
     {
         $code = '';
         foreach ($this->_recipes as $item) {
@@ -123,17 +119,17 @@ class Ingo_Script_Maildrop extends Ingo_Script {
      *
      * @return string  The maildrop script.
      */
-    function generate()
+    public function generate()
     {
         $filters = $GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FILTERS);
 
-        $this->addItem(new Maildrop_Comment(_("maildrop script generated by Ingo") . ' (' . date('F j, Y, g:i a') . ')'));
+        $this->addItem(new Ingo_Script_Maildrop_Comment(_("maildrop script generated by Ingo") . ' (' . date('F j, Y, g:i a') . ')'));
 
         /* Add variable information, if present. */
         if (!empty($this->_params['variables']) &&
             is_array($this->_params['variables'])) {
             foreach ($this->_params['variables'] as $key => $val) {
-                $this->addItem(new Maildrop_Variable(array('name' => $key, 'value' => $val)));
+                $this->addItem(new Ingo_Script_Maildrop_Variable(array('name' => $key, 'value' => $val)));
             }
         }
 
@@ -162,11 +158,11 @@ class Ingo_Script_Maildrop extends Ingo_Script {
             default:
                 if (in_array($filter['action'], $this->_actions)) {
                     /* Create filter if using AND. */
-                    $recipe = new Maildrop_Recipe($filter, $this->_params);
+                    $recipe = new Ingo_Script_Maildrop_Recipe($filter, $this->_params);
                     foreach ($filter['conditions'] as $condition) {
                         $recipe->addCondition($condition);
                     }
-                    $this->addItem(new Maildrop_Comment($filter['name'], !empty($filter['disable']), true));
+                    $this->addItem(new Ingo_Script_Maildrop_Comment($filter['name'], !empty($filter['disable']), true));
                     $this->addItem($recipe);
                 }
             }
@@ -181,23 +177,25 @@ class Ingo_Script_Maildrop extends Ingo_Script {
      *
      * @param boolean $disable  Disable the blacklist?
      */
-    function generateBlacklist($disable = false)
+    public function generateBlacklist($disable = false)
     {
-        $blacklist = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_BLACKLIST);
+        $blacklist = $GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_BLACKLIST);
         $bl_addr = $blacklist->getBlacklist();
         $bl_folder = $blacklist->getBlacklistFolder();
 
-        $bl_type = (empty($bl_folder)) ? Ingo_Storage::ACTION_DISCARD : Ingo_Storage::ACTION_MOVE;
+        $bl_type = empty($bl_folder)
+            ? Ingo_Storage::ACTION_DISCARD
+            : Ingo_Storage::ACTION_MOVE;
 
         if (!empty($bl_addr)) {
-            $this->addItem(new Maildrop_Comment(_("Blacklisted Addresses"), $disable, true));
+            $this->addItem(new Ingo_Script_Maildrop_Comment(_("Blacklisted Addresses"), $disable, true));
             $params = array('action-value' => $bl_folder,
                             'action' => $bl_type,
                             'disable' => $disable);
 
             foreach ($bl_addr as $address) {
                 if (!empty($address)) {
-                    $recipe = new Maildrop_Recipe($params, $this->_params);
+                    $recipe = new Ingo_Script_Maildrop_Recipe($params, $this->_params);
                     $recipe->addCondition(array('field' => 'From', 'value' => $address));
                     $this->addItem($recipe);
                 }
@@ -211,16 +209,16 @@ class Ingo_Script_Maildrop extends Ingo_Script {
      *
      * @param boolean $disable  Disable the whitelist?
      */
-    function generateWhitelist($disable = false)
+    public function generateWhitelist($disable = false)
     {
-        $whitelist = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_WHITELIST);
+        $whitelist = $GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_WHITELIST);
         $wl_addr = $whitelist->getWhitelist();
 
         if (!empty($wl_addr)) {
-            $this->addItem(new Maildrop_Comment(_("Whitelisted Addresses"), $disable, true));
+            $this->addItem(new Ingo_Script_Maildrop_Comment(_("Whitelisted Addresses"), $disable, true));
             foreach ($wl_addr as $address) {
                 if (!empty($address)) {
-                    $recipe = new Maildrop_Recipe(array('action' => Ingo_Storage::ACTION_KEEP, 'disable' => $disable), $this->_params);
+                    $recipe = new Ingo_Script_Maildrop_Recipe(array('action' => Ingo_Storage::ACTION_KEEP, 'disable' => $disable), $this->_params);
                     $recipe->addCondition(array('field' => 'From', 'value' => $address));
                     $this->addItem($recipe);
                 }
@@ -233,20 +231,20 @@ class Ingo_Script_Maildrop extends Ingo_Script {
      *
      * @param boolean $disable  Disable forwarding?
      */
-    function generateForward($disable = false)
+    public function generateForward($disable = false)
     {
-        $forward = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FORWARD);
+        $forward = $GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FORWARD);
         $addresses = $forward->getForwardAddresses();
 
         if (!empty($addresses)) {
-            $this->addItem(new Maildrop_Comment(_("Forwards"), $disable, true));
+            $this->addItem(new Ingo_Script_Maildrop_Comment(_("Forwards"), $disable, true));
             $params = array('action' => Ingo_Storage::ACTION_FORWARD,
                             'action-value' => $addresses,
                             'disable' => $disable);
             if ($forward->getForwardKeep()) {
-                $params['action'] = MAILDROP_STORAGE_ACTION_STOREANDFORWARD;
+                $params['action'] = self::MAILDROP_STORAGE_ACTION_STOREANDFORWARD;
             }
-            $recipe = new Maildrop_Recipe($params, $this->_params);
+            $recipe = new Ingo_Script_Maildrop_Recipe($params, $this->_params);
             $recipe->addCondition(array('field' => 'From', 'value' => ''));
             $this->addItem($recipe);
         }
@@ -257,9 +255,9 @@ class Ingo_Script_Maildrop extends Ingo_Script {
      *
      * @param boolean $disable  Disable forwarding?
      */
-    function generateVacation($disable = false)
+    public function generateVacation($disable = false)
     {
-        $vacation = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_VACATION);
+        $vacation = $GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_VACATION);
         $addresses = $vacation->getVacationAddresses();
         $actionval = array('addresses' => $addresses,
                            'subject' => $vacation->getVacationSubject(),
@@ -271,23 +269,24 @@ class Ingo_Script_Maildrop extends Ingo_Script {
                            'end' => $vacation->getVacationEnd());
 
         if (!empty($addresses)) {
-            $this->addItem(new Maildrop_Comment(_("Vacation"), $disable, true));
+            $this->addItem(new Ingo_Script_Maildrop_Comment(_("Vacation"), $disable, true));
             $params = array('action' => Ingo_Storage::ACTION_VACATION,
                             'action-value' => $actionval,
                             'disable' => $disable);
-            $recipe = new Maildrop_Recipe($params, $this->_params);
+            $recipe = new Ingo_Script_Maildrop_Recipe($params, $this->_params);
             $this->addItem($recipe);
         }
     }
 
     /**
-     * Generates the maildrop script to handle spam as identified by SpamAssassin
+     * Generates the maildrop script to handle spam as identified by
+     * SpamAssassin.
      *
      * @param boolean $disable  Disable the spam-filter?
      */
-    function generateSpamfilter($disable = false)
+    public function generateSpamfilter($disable = false)
     {
-        $spam = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_SPAM);
+        $spam = $GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_SPAM);
         if ($spam == false) {
             return;
         }
@@ -295,12 +294,12 @@ class Ingo_Script_Maildrop extends Ingo_Script {
         $spam_folder = $spam->getSpamFolder();
         $spam_action = (empty($spam_folder)) ? Ingo_Storage::ACTION_DISCARD : Ingo_Storage::ACTION_MOVE;
 
-        $this->addItem(new Maildrop_Comment(_("Spam Filter"), $disable, true));
+        $this->addItem(new Ingo_Script_Maildrop_Comment(_("Spam Filter"), $disable, true));
 
         $params = array('action-value' => $spam_folder,
                         'action' => $spam_action,
                         'disable' => $disable);
-        $recipe = new Maildrop_Recipe($params, $this->_params);
+        $recipe = new Ingo_Script_Maildrop_Recipe($params, $this->_params);
         if ($this->_params['spam_compare'] == 'numeric') {
             $recipe->addCondition(array('match' => 'greater than or equal to',
                                         'field' => $this->_params['spam_header'],
@@ -320,440 +319,9 @@ class Ingo_Script_Maildrop extends Ingo_Script {
      * @param object $item  The item to add to the recipe list.
      *                      The object should have a generate() function.
      */
-    function addItem($item)
+    public function addItem($item)
     {
         $this->_recipes[] = $item;
     }
 
 }
-
-/**
- * The Maildrop_Comment:: class represents a maildrop comment.  This is
- * a pretty simple class, but it makes the code in Ingo_Script_Maildrop::
- * cleaner as it provides a generate() function and can be added to the
- * recipe list the same way as a recipe can be.
- *
- * @author  Matt Weyland <mathias@weyland.ch>
- * @package Ingo
- */
-class Maildrop_Comment {
-
-    /**
-     * The comment text.
-     *
-     * @var string
-     */
-    var $_comment = '';
-
-    /**
-     * Constructs a new maildrop comment.
-     *
-     * @param string $comment   Comment to be generated.
-     * @param boolean $disable  Output 'DISABLED' comment?
-     * @param boolean $header   Output a 'header' comment?
-     */
-    function Maildrop_Comment($comment, $disable = false, $header = false)
-    {
-        if ($disable) {
-            $comment = _("DISABLED: ") . $comment;
-        }
-
-        if ($header) {
-            $this->_comment .= "##### $comment #####";
-        } else {
-            $this->_comment .= "# $comment";
-        }
-    }
-
-    /**
-     * Returns the comment stored by this object.
-     *
-     * @return string  The comment stored by this object.
-     */
-    function generate()
-    {
-        return $this->_comment;
-    }
-
-}
-
-/**
- * The Maildrop_Recipe:: class represents a maildrop recipe.
- *
- * @author  Matt Weyland <mathias@weyland.ch>
- * @package Ingo
- */
-class Maildrop_Recipe {
-
-    var $_action = array();
-    var $_conditions = array();
-    var $_disable = '';
-    var $_flags = '';
-    var $_params = array();
-    var $_combine = '';
-    var $_valid = true;
-
-    var $_operators = array(
-        'less than'                => '<',
-        'less than or equal to'    => '<=',
-        'equal'                    => '==',
-        'not equal'                => '!=',
-        'greater than'             => '>',
-        'greater than or equal to' => '>=',
-    );
-
-    /**
-     * Constructs a new maildrop recipe.
-     *
-     * @param array $params        Array of parameters.
-     *                             REQUIRED FIELDS:
-     *                             'action'
-     *                             OPTIONAL FIELDS:
-     *                             'action-value' (only used if the
-     *                             'action' requires it)
-     * @param array $scriptparams  Array of parameters passed to
-     *                             Ingo_Script_Maildrop::.
-     */
-    function Maildrop_Recipe($params = array(), $scriptparams = array())
-    {
-        $this->_disable = !empty($params['disable']);
-        $this->_params = $scriptparams;
-        $this->_action[] = 'exception {';
-
-        switch ($params['action']) {
-        case Ingo_Storage::ACTION_KEEP:
-            $this->_action[] = '   to "${DEFAULT}"';
-            break;
-
-        case Ingo_Storage::ACTION_MOVE:
-            $this->_action[] = '   to ' . $this->maildropPath($params['action-value']);
-            break;
-
-        case Ingo_Storage::ACTION_DISCARD:
-            $this->_action[] = '   exit';
-            break;
-
-        case Ingo_Storage::ACTION_REDIRECT:
-            $this->_action[] = '   to "! ' . $params['action-value'] . '"';
-            break;
-
-        case Ingo_Storage::ACTION_REDIRECTKEEP:
-            $this->_action[] = '   cc "! ' . $params['action-value'] . '"';
-            $this->_action[] = '   to "${DEFAULT}"';
-            break;
-
-        case Ingo_Storage::ACTION_REJECT:
-            $this->_action[] = '   EXITCODE=77'; # EX_NOPERM (permanent failure)
-            $this->_action[] = '   echo "5.7.1 ' . $params['action-value'] . '"';
-            $this->_action[] = '   exit';
-            break;
-
-        case Ingo_Storage::ACTION_VACATION:
-            $from = '';
-            foreach ($params['action-value']['addresses'] as $address) {
-                $from = $address;
-            }
-
-            /**
-             * @TODO
-             *
-             * Exclusion and listfilter
-             */
-            $exclude = '';
-            foreach ($params['action-value']['excludes'] as $address) {
-                $exclude .= $address . ' ';
-            }
-
-            $start = strftime($params['action-value']['start']);
-            if ($start === false) {
-                $start = 0;
-            }
-            $end = strftime($params['action-value']['end']);
-            if ($end === false) {
-                $end = 0;
-            }
-            $days = strftime($params['action-value']['days']);
-            if ($days === false) {
-                // Set to same value as $_days in ingo/lib/Storage.php
-                $days = 7;
-            }
-
-            // Writing vacation.msg file
-            $reason = Horde_Mime::encode($params['action-value']['reason'], $scriptparams['charset']);
-            $driver = Ingo::getDriver();
-            $driver->_connect();
-            $result = $driver->_vfs->writeData($driver->_params['vfs_path'], 'vacation.msg', $reason, true);
-
-            // Rule : Do not send responses to bulk or list messages
-            if ($params['action-value']['ignorelist'] == 1) {
-                $params['combine'] = Ingo_Storage::COMBINE_ALL;
-                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Precedence: (bulk|list|junk)/'));
-                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Return-Path:.*<#@\[\]>/'));
-                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Return-Path:.*<>/'));
-                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^From:.*MAILER-DAEMON/'));
-                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^X-ClamAV-Notice-Flag: *YES/'));
-                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Content-Type:.*message\/delivery-status/'));
-                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Delivery Status Notification/'));
-                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Undelivered Mail Returned to Sender/'));
-                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Delivery failure/'));
-                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Message delay/'));
-                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Mail Delivery Subsystem/'));
-                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Mail System Error.*Returned Mail/'));
-                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^X-Spam-Flag: YES/ '));
-            } else {
-                $this->addCondition(array('field' => 'From', 'value' => ''));
-            }
-
-            // Rule : Start/End of vacation
-            if (($start != 0) && ($end !== 0)) {
-                $this->_action[] = '  flock "vacationprocess.lock" {';
-                $this->_action[] = '    current_time=time';
-                $this->_action[] = '      if ( \ ';
-                $this->_action[] = '        ($current_time >= ' . $start . ') && \ ';
-                $this->_action[] = '        ($current_time <= ' . $end . ')) ';
-                $this->_action[] = '      {';
-            }
-            $this->_action[] = "  cc \"| mailbot -D " . $params['action-value']['days'] . " -c '" . $scriptparams['charset'] . "' -t \$HOME/vacation.msg -d \$HOME/vacation -A 'From: $from' -s '" . Horde_Mime::encode($params['action-value']['subject'], $scriptparams['charset'])  . "' /usr/sbin/sendmail -t \"";
-            if (($start != 0) && ($end !== 0)) {
-                $this->_action[] = '      }';
-                $this->_action[] = '  }';
-            }
-
-            break;
-
-        case Ingo_Storage::ACTION_FORWARD:
-        case MAILDROP_STORAGE_ACTION_STOREANDFORWARD:
-            foreach ($params['action-value'] as $address) {
-                if (!empty($address)) {
-                    $this->_action[] = '  cc "! ' . $address . '"';
-                }
-            }
-
-            /* The 'to' must be the last action, because maildrop
-             * stops processing after it. */
-            if ($params['action'] == MAILDROP_STORAGE_ACTION_STOREANDFORWARD) {
-                $this->_action[] = ' to "${DEFAULT}"';
-            } else {
-                $this->_action[] = ' exit';
-            }
-            break;
-
-        default:
-            $this->_valid = false;
-            break;
-        }
-
-        $this->_action[] = '}';
-
-        if (isset($params['combine']) &&
-            ($params['combine'] == Ingo_Storage::COMBINE_ALL)) {
-            $this->_combine = '&& ';
-        } else {
-            $this->_combine = '|| ';
-        }
-    }
-
-    /**
-     * Adds a flag to the recipe.
-     *
-     * @param string $flag  String of flags to append to the current flags.
-     */
-    function addFlag($flag)
-    {
-        $this->_flags .= $flag;
-    }
-
-    /**
-     * Adds a condition to the recipe.
-     *
-     * @param optonal array $condition  Array of parameters. Required keys
-     *                                  are 'field' and 'value'. 'case' is
-     *                                  an optional keys.
-     */
-    function addCondition($condition = array())
-    {
-        $flag = (!empty($condition['case'])) ? 'D' : '';
-        if (empty($this->_conditions)) {
-            $this->addFlag($flag);
-        }
-
-        $string = '';
-        $extra = '';
-
-        $match = (isset($condition['match'])) ? $condition['match'] : null;
-        // negate tests starting with 'not ', except 'not equals', which simply uses the != operator
-        if ($match != 'not equal' && substr($match, 0, 4) == 'not ') {
-            $string .= '! ';
-        }
-
-        // convert 'field' to PCRE pattern matching
-        if (strpos($condition['field'], ',') == false) {
-            $string .= '/^' . $condition['field'] . ':\\s*';
-        } else {
-            $string .= '/^(' . str_replace(',', '|', $condition['field']) . '):\\s*';
-        }
-
-        switch ($match) {
-        case 'not regex':
-        case 'regex':
-            $string .= $condition['value'] . '/:h';
-            break;
-
-        case 'filter':
-            $string = $condition['value'];
-            break;
-
-        case 'exists':
-        case 'not exist':
-            // Just run a match for the header name
-            $string .= '/:h';
-            break;
-
-        case 'less than or equal to':
-        case 'less than':
-        case 'equal':
-        case 'not equal':
-        case 'greater than or equal to':
-        case 'greater than':
-            $string .= '(\d+(\.\d+)?)/:h';
-            $extra = ' && $MATCH1 ' . $this->_operators[$match] . ' ' . (int)$condition['value'];
-            break;
-
-        case 'begins with':
-        case 'not begins with':
-            $string .= preg_quote($condition['value'], '/') . '/:h';
-            break;
-
-        case 'ends with':
-        case 'not ends with':
-            $string .= '.*' . preg_quote($condition['value'], '/') . '$/:h';
-            break;
-
-        case 'is':
-        case 'not is':
-            $string .= preg_quote($condition['value'], '/') . '$/:h';
-            break;
-
-        case 'matches':
-        case 'not matches':
-            $string .= str_replace(array('\\*', '\\?'), array('.*', '.'), preg_quote($condition['value'], '/') . '$') . '/:h';
-            break;
-
-        case 'contains':
-        case 'not contain':
-        default:
-            $string .= '.*' . preg_quote($condition['value'], '/') . '/:h';
-            break;
-        }
-
-        $this->_conditions[] = array('condition' => $string, 'flags' => $flag, 'extra' => $extra);
-    }
-
-    /**
-     * Generates maildrop code to represent the recipe.
-     *
-     * @return string  maildrop code to represent the recipe.
-     */
-    function generate()
-    {
-        $text = array();
-
-        if (!$this->_valid) {
-            return '';
-        }
-
-        if (count($this->_conditions) > 0) {
-
-            $text[] = "if( \\";
-
-            $nest = false;
-            foreach ($this->_conditions as $condition) {
-                $cond = $nest ? $this->_combine : '   ';
-                $text[] = $cond . $condition['condition'] . $condition['flags'] . $condition['extra'] . " \\";
-                $nest = true;
-            }
-
-            $text[] = ')';
-        }
-
-        foreach ($this->_action as $val) {
-            $text[] = $val;
-        }
-
-        if ($this->_disable) {
-            $code = '';
-            foreach ($text as $val) {
-                $comment = new Maildrop_Comment($val);
-                $code .= $comment->generate() . "\n";
-            }
-            return $code . "\n";
-        } else {
-            return implode("\n", $text) . "\n";
-        }
-    }
-
-    /**
-     * Returns a maildrop-ready mailbox path, converting IMAP folder pathname
-     * conventions as necessary.
-     *
-     * @param string $folder  The IMAP folder name.
-     *
-     * @return string  The maildrop mailbox path.
-     */
-    function maildropPath($folder)
-    {
-        /* NOTE: '$DEFAULT' here is a literal, not a PHP variable. */
-        if (isset($this->_params) &&
-            ($this->_params['path_style'] == 'maildir')) {
-            if (empty($folder) || ($folder == 'INBOX')) {
-                return '"${DEFAULT}"';
-            }
-            if ($this->_params['strip_inbox'] &&
-                substr($folder, 0, 6) == 'INBOX.') {
-                $folder = substr($folder, 6);
-            }
-            return '"${DEFAULT}/.' . $folder . '/"';
-        } else {
-            if (empty($folder) || ($folder == 'INBOX')) {
-                return '${DEFAULT}';
-            }
-            return str_replace(' ', '\ ', $folder);
-        }
-    }
-
-}
-
-/**
- * The Maildrop_Variable:: class represents a Maildrop variable.
- *
- * @author  Matt Weyland <mathias@weyland.ch>
- * @package Ingo
- */
-class Maildrop_Variable {
-
-    var $_name;
-    var $_value;
-
-    /**
-     * Constructs a new maildrop variable.
-     *
-     * @param array $params  Array of parameters. Expected fields are 'name'
-     *                       and 'value'.
-     */
-    function Maildrop_Variable($params = array())
-    {
-        $this->_name = $params['name'];
-        $this->_value = $params['value'];
-    }
-
-    /**
-     * Generates maildrop code to represent the variable.
-     *
-     * @return string  maildrop code to represent the variable.
-     */
-    function generate()
-    {
-        return $this->_name . '=' . $this->_value . "\n";
-    }
-
-}
diff --git a/ingo/lib/Script/Maildrop/Comment.php b/ingo/lib/Script/Maildrop/Comment.php
new file mode 100644 (file)
index 0000000..ba43f47
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+/**
+ * The Ingo_Script_Maildrop_Comment:: class represents a maildrop comment.
+ * This is a pretty simple class, but it makes the code in
+ * Ingo_Script_Maildrop:: cleaner as it provides a generate() function and can
+ * be added to the recipe list the same way as a recipe can be.
+ *
+ * Copyright 2005-2007 Matt Weyland <mathias@weyland.ch>
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Matt Weyland <mathias@weyland.ch>
+ * @package Ingo
+ */
+class Ingo_Script_Maildrop_Comment
+{
+    /**
+     * The comment text.
+     *
+     * @var string
+     */
+    protected $_comment = '';
+
+    /**
+     * Constructs a new maildrop comment.
+     *
+     * @param string $comment   Comment to be generated.
+     * @param boolean $disable  Output 'DISABLED' comment?
+     * @param boolean $header   Output a 'header' comment?
+     */
+    public function __construct($comment, $disable = false, $header = false)
+    {
+        if ($disable) {
+            $comment = _("DISABLED: ") . $comment;
+        }
+
+        $this->_comment = $header
+            ? '##### ' . $comment . ' #####'
+            : '# ' . $comment;
+    }
+
+    /**
+     * Returns the comment stored by this object.
+     *
+     * @return string  The comment stored by this object.
+     */
+    public function generate()
+    {
+        return $this->_comment;
+    }
+
+}
diff --git a/ingo/lib/Script/Maildrop/Recipe.php b/ingo/lib/Script/Maildrop/Recipe.php
new file mode 100644 (file)
index 0000000..c88dfb3
--- /dev/null
@@ -0,0 +1,373 @@
+<?php
+/**
+ * The Ingo_Script_Maildrop_Recipe:: class represents a maildrop recipe.
+ *
+ * Copyright 2005-2007 Matt Weyland <mathias@weyland.ch>
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Matt Weyland <mathias@weyland.ch>
+ * @package Ingo
+ */
+class Maildrop_Recipe
+{
+    /**
+     */
+    protected $_action = array();
+
+    /**
+     */
+    protected $_conditions = array();
+
+    /**
+     */
+    protected $_disable = '';
+
+    /**
+     */
+    protected $_flags = '';
+
+    /**
+     */
+    protected $_params = array();
+
+    /**
+     */
+    protected $_combine = '';
+
+    /**
+     */
+    protected $_valid = true;
+
+    /**
+     */
+    protected $_operators = array(
+        'less than'                => '<',
+        'less than or equal to'    => '<=',
+        'equal'                    => '==',
+        'not equal'                => '!=',
+        'greater than'             => '>',
+        'greater than or equal to' => '>=',
+    );
+
+    /**
+     * Constructs a new maildrop recipe.
+     *
+     * @param array $params        Array of parameters.
+     *                             REQUIRED FIELDS:
+     *                             'action'
+     *                             OPTIONAL FIELDS:
+     *                             'action-value' (only used if the
+     *                             'action' requires it)
+     * @param array $scriptparams  Array of parameters passed to
+     *                             Ingo_Script_Maildrop::.
+     */
+    public function __construct($params = array(), $scriptparams = array())
+    {
+        $this->_disable = !empty($params['disable']);
+        $this->_params = $scriptparams;
+        $this->_action[] = 'exception {';
+
+        switch ($params['action']) {
+        case Ingo_Storage::ACTION_KEEP:
+            $this->_action[] = '   to "${DEFAULT}"';
+            break;
+
+        case Ingo_Storage::ACTION_MOVE:
+            $this->_action[] = '   to ' . $this->maildropPath($params['action-value']);
+            break;
+
+        case Ingo_Storage::ACTION_DISCARD:
+            $this->_action[] = '   exit';
+            break;
+
+        case Ingo_Storage::ACTION_REDIRECT:
+            $this->_action[] = '   to "! ' . $params['action-value'] . '"';
+            break;
+
+        case Ingo_Storage::ACTION_REDIRECTKEEP:
+            $this->_action[] = '   cc "! ' . $params['action-value'] . '"';
+            $this->_action[] = '   to "${DEFAULT}"';
+            break;
+
+        case Ingo_Storage::ACTION_REJECT:
+            $this->_action[] = '   EXITCODE=77'; # EX_NOPERM (permanent failure)
+            $this->_action[] = '   echo "5.7.1 ' . $params['action-value'] . '"';
+            $this->_action[] = '   exit';
+            break;
+
+        case Ingo_Storage::ACTION_VACATION:
+            $from = '';
+            foreach ($params['action-value']['addresses'] as $address) {
+                $from = $address;
+            }
+
+            /**
+             * @TODO
+             *
+             * Exclusion and listfilter
+             */
+            $exclude = '';
+            foreach ($params['action-value']['excludes'] as $address) {
+                $exclude .= $address . ' ';
+            }
+
+            $start = strftime($params['action-value']['start']);
+            if ($start === false) {
+                $start = 0;
+            }
+            $end = strftime($params['action-value']['end']);
+            if ($end === false) {
+                $end = 0;
+            }
+            $days = strftime($params['action-value']['days']);
+            if ($days === false) {
+                // Set to same value as $_days in ingo/lib/Storage.php
+                $days = 7;
+            }
+
+            // Writing vacation.msg file
+            $reason = Horde_Mime::encode($params['action-value']['reason'], $scriptparams['charset']);
+            $driver = Ingo::getDriver();
+            $driver->_connect();
+            $result = $driver->_vfs->writeData($driver->_params['vfs_path'], 'vacation.msg', $reason, true);
+
+            // Rule : Do not send responses to bulk or list messages
+            if ($params['action-value']['ignorelist'] == 1) {
+                $params['combine'] = Ingo_Storage::COMBINE_ALL;
+                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Precedence: (bulk|list|junk)/'));
+                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Return-Path:.*<#@\[\]>/'));
+                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Return-Path:.*<>/'));
+                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^From:.*MAILER-DAEMON/'));
+                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^X-ClamAV-Notice-Flag: *YES/'));
+                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Content-Type:.*message\/delivery-status/'));
+                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Delivery Status Notification/'));
+                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Undelivered Mail Returned to Sender/'));
+                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Delivery failure/'));
+                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Message delay/'));
+                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Mail Delivery Subsystem/'));
+                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Mail System Error.*Returned Mail/'));
+                $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^X-Spam-Flag: YES/ '));
+            } else {
+                $this->addCondition(array('field' => 'From', 'value' => ''));
+            }
+
+            // Rule : Start/End of vacation
+            if (($start != 0) && ($end !== 0)) {
+                $this->_action[] = '  flock "vacationprocess.lock" {';
+                $this->_action[] = '    current_time=time';
+                $this->_action[] = '      if ( \ ';
+                $this->_action[] = '        ($current_time >= ' . $start . ') && \ ';
+                $this->_action[] = '        ($current_time <= ' . $end . ')) ';
+                $this->_action[] = '      {';
+            }
+            $this->_action[] = "  cc \"| mailbot -D " . $params['action-value']['days'] . " -c '" . $scriptparams['charset'] . "' -t \$HOME/vacation.msg -d \$HOME/vacation -A 'From: $from' -s '" . Horde_Mime::encode($params['action-value']['subject'], $scriptparams['charset'])  . "' /usr/sbin/sendmail -t \"";
+            if (($start != 0) && ($end !== 0)) {
+                $this->_action[] = '      }';
+                $this->_action[] = '  }';
+            }
+
+            break;
+
+        case Ingo_Storage::ACTION_FORWARD:
+        case Ingo_Script_Maildrop::MAILDROP_STORAGE_ACTION_STOREANDFORWARD:
+            foreach ($params['action-value'] as $address) {
+                if (!empty($address)) {
+                    $this->_action[] = '  cc "! ' . $address . '"';
+                }
+            }
+
+            /* The 'to' must be the last action, because maildrop
+             * stops processing after it. */
+            if ($params['action'] == Ingo_Script_Maildrop::MAILDROP_STORAGE_ACTION_STOREANDFORWARD) {
+                $this->_action[] = ' to "${DEFAULT}"';
+            } else {
+                $this->_action[] = ' exit';
+            }
+            break;
+
+        default:
+            $this->_valid = false;
+            break;
+        }
+
+        $this->_action[] = '}';
+
+        if (isset($params['combine']) &&
+            ($params['combine'] == Ingo_Storage::COMBINE_ALL)) {
+            $this->_combine = '&& ';
+        } else {
+            $this->_combine = '|| ';
+        }
+    }
+
+    /**
+     * Adds a flag to the recipe.
+     *
+     * @param string $flag  String of flags to append to the current flags.
+     */
+    public function addFlag($flag)
+    {
+        $this->_flags .= $flag;
+    }
+
+    /**
+     * Adds a condition to the recipe.
+     *
+     * @param optonal array $condition  Array of parameters. Required keys
+     *                                  are 'field' and 'value'. 'case' is
+     *                                  an optional keys.
+     */
+    public function addCondition($condition = array())
+    {
+        $flag = (!empty($condition['case'])) ? 'D' : '';
+        if (empty($this->_conditions)) {
+            $this->addFlag($flag);
+        }
+
+        $string = '';
+        $extra = '';
+
+        $match = (isset($condition['match'])) ? $condition['match'] : null;
+        // negate tests starting with 'not ', except 'not equals', which simply uses the != operator
+        if ($match != 'not equal' && substr($match, 0, 4) == 'not ') {
+            $string .= '! ';
+        }
+
+        // convert 'field' to PCRE pattern matching
+        if (strpos($condition['field'], ',') == false) {
+            $string .= '/^' . $condition['field'] . ':\\s*';
+        } else {
+            $string .= '/^(' . str_replace(',', '|', $condition['field']) . '):\\s*';
+        }
+
+        switch ($match) {
+        case 'not regex':
+        case 'regex':
+            $string .= $condition['value'] . '/:h';
+            break;
+
+        case 'filter':
+            $string = $condition['value'];
+            break;
+
+        case 'exists':
+        case 'not exist':
+            // Just run a match for the header name
+            $string .= '/:h';
+            break;
+
+        case 'less than or equal to':
+        case 'less than':
+        case 'equal':
+        case 'not equal':
+        case 'greater than or equal to':
+        case 'greater than':
+            $string .= '(\d+(\.\d+)?)/:h';
+            $extra = ' && $MATCH1 ' . $this->_operators[$match] . ' ' . (int)$condition['value'];
+            break;
+
+        case 'begins with':
+        case 'not begins with':
+            $string .= preg_quote($condition['value'], '/') . '/:h';
+            break;
+
+        case 'ends with':
+        case 'not ends with':
+            $string .= '.*' . preg_quote($condition['value'], '/') . '$/:h';
+            break;
+
+        case 'is':
+        case 'not is':
+            $string .= preg_quote($condition['value'], '/') . '$/:h';
+            break;
+
+        case 'matches':
+        case 'not matches':
+            $string .= str_replace(array('\\*', '\\?'), array('.*', '.'), preg_quote($condition['value'], '/') . '$') . '/:h';
+            break;
+
+        case 'contains':
+        case 'not contain':
+        default:
+            $string .= '.*' . preg_quote($condition['value'], '/') . '/:h';
+            break;
+        }
+
+        $this->_conditions[] = array('condition' => $string, 'flags' => $flag, 'extra' => $extra);
+    }
+
+    /**
+     * Generates maildrop code to represent the recipe.
+     *
+     * @return string  maildrop code to represent the recipe.
+     */
+    public function generate()
+    {
+        $text = array();
+
+        if (!$this->_valid) {
+            return '';
+        }
+
+        if (count($this->_conditions) > 0) {
+
+            $text[] = "if( \\";
+
+            $nest = false;
+            foreach ($this->_conditions as $condition) {
+                $cond = $nest ? $this->_combine : '   ';
+                $text[] = $cond . $condition['condition'] . $condition['flags'] . $condition['extra'] . " \\";
+                $nest = true;
+            }
+
+            $text[] = ')';
+        }
+
+        foreach ($this->_action as $val) {
+            $text[] = $val;
+        }
+
+        if ($this->_disable) {
+            $code = '';
+            foreach ($text as $val) {
+                $comment = new Ingo_Script_Maildrop_Comment($val);
+                $code .= $comment->generate() . "\n";
+            }
+            return $code . "\n";
+        } else {
+            return implode("\n", $text) . "\n";
+        }
+    }
+
+    /**
+     * Returns a maildrop-ready mailbox path, converting IMAP folder pathname
+     * conventions as necessary.
+     *
+     * @param string $folder  The IMAP folder name.
+     *
+     * @return string  The maildrop mailbox path.
+     */
+    public function maildropPath($folder)
+    {
+        /* NOTE: '$DEFAULT' here is a literal, not a PHP variable. */
+        if (isset($this->_params) &&
+            ($this->_params['path_style'] == 'maildir')) {
+            if (empty($folder) || ($folder == 'INBOX')) {
+                return '"${DEFAULT}"';
+            }
+            if ($this->_params['strip_inbox'] &&
+                substr($folder, 0, 6) == 'INBOX.') {
+                $folder = substr($folder, 6);
+            }
+            return '"${DEFAULT}/.' . $folder . '/"';
+        } else {
+            if (empty($folder) || ($folder == 'INBOX')) {
+                return '${DEFAULT}';
+            }
+            return str_replace(' ', '\ ', $folder);
+        }
+    }
+
+}
diff --git a/ingo/lib/Script/Maildrop/Variable.php b/ingo/lib/Script/Maildrop/Variable.php
new file mode 100644 (file)
index 0000000..0545088
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+/**
+ * The Ingo_Script_Maildrop_Variable:: class represents a Maildrop variable.
+ *
+ * Copyright 2005-2007 Matt Weyland <mathias@weyland.ch>
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Matt Weyland <mathias@weyland.ch>
+ * @package Ingo
+ */
+class Maildrop_Variable
+{
+    /**
+     */
+    protected $_name;
+
+    /**
+     */
+    protected $_value;
+
+    /**
+     * Constructs a new maildrop variable.
+     *
+     * @param array $params  Array of parameters. Expected fields are 'name'
+     *                       and 'value'.
+     */
+    public function __construct($params = array())
+    {
+        $this->_name = $params['name'];
+        $this->_value = $params['value'];
+    }
+
+    /**
+     * Generates maildrop code to represent the variable.
+     *
+     * @return string  maildrop code to represent the variable.
+     */
+    public function generate()
+    {
+        return $this->_name . '=' . $this->_value . "\n";
+    }
+
+}
index 6507f97..c5d18a3 100644 (file)
  * @author  Ben Chavet <ben@horde.org>
  * @package Ingo
  */
-class Ingo_Script_Procmail extends Ingo_Script {
-
+class Ingo_Script_Procmail extends Ingo_Script
+{
     /**
      * The list of actions allowed (implemented) for this driver.
      *
      * @var array
      */
-    var $_actions = array(
+    protected $_actions = array(
         Ingo_Storage::ACTION_KEEP,
         Ingo_Storage::ACTION_MOVE,
         Ingo_Storage::ACTION_DISCARD,
@@ -32,7 +32,7 @@ class Ingo_Script_Procmail extends Ingo_Script {
      *
      * @var array
      */
-    var $_categories = array(
+    protected $_categories = array(
         Ingo_Storage::ACTION_BLACKLIST,
         Ingo_Storage::ACTION_WHITELIST,
         Ingo_Storage::ACTION_VACATION,
@@ -44,7 +44,7 @@ class Ingo_Script_Procmail extends Ingo_Script {
      *
      * @var array
      */
-    var $_types = array(
+    protected $_types = array(
         Ingo_Storage::TYPE_HEADER,
         Ingo_Storage::TYPE_BODY
     );
@@ -54,7 +54,7 @@ class Ingo_Script_Procmail extends Ingo_Script {
      *
      * @var array
      */
-    var $_special_types = array(
+    protected $_special_types = array(
         'Destination',
     );
 
@@ -63,7 +63,7 @@ class Ingo_Script_Procmail extends Ingo_Script {
      *
      * @var array
      */
-    var $_tests = array(
+    protected $_tests = array(
         'contains',
         'not contain',
         'begins with',
@@ -78,35 +78,35 @@ class Ingo_Script_Procmail extends Ingo_Script {
      *
      * @var boolean
      */
-    var $_casesensitive = true;
+    protected $_casesensitive = true;
 
     /**
      * Does the driver support the stop-script option?
      *
      * @var boolean
      */
-    var $_supportStopScript = true;
+    protected $_supportStopScript = true;
 
     /**
      * Does the driver require a script file to be generated?
      *
      * @var boolean
      */
-    var $_scriptfile = true;
+    protected $_scriptfile = true;
 
     /**
      * The recipes that make up the code.
      *
      * @var array
      */
-    var $_recipes = array();
+    protected $_recipes = array();
 
     /**
      * Returns a script previously generated with generate().
      *
      * @return string  The procmail script.
      */
-    function toCode()
+    public function toCode()
     {
         $code = '';
         foreach ($this->_recipes as $item) {
@@ -131,17 +131,17 @@ class Ingo_Script_Procmail extends Ingo_Script {
      *
      * @return string  The procmail script.
      */
-    function generate()
+    public function generate()
     {
         $filters = $GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FILTERS);
 
-        $this->addItem(new Procmail_Comment(_("procmail script generated by Ingo") . ' (' . date('F j, Y, g:i a') . ')'));
+        $this->addItem(new Ingo_Script_Procmail_Comment(_("procmail script generated by Ingo") . ' (' . date('F j, Y, g:i a') . ')'));
 
         /* Add variable information, if present. */
         if (!empty($this->_params['variables']) &&
             is_array($this->_params['variables'])) {
             foreach ($this->_params['variables'] as $key => $val) {
-                $this->addItem(new Procmail_Variable(array('name' => $key, 'value' => $val)));
+                $this->addItem(new Ingo_Script_Procmail_Variable(array('name' => $key, 'value' => $val)));
             }
         }
 
@@ -167,21 +167,21 @@ class Ingo_Script_Procmail extends Ingo_Script {
                 if (in_array($filter['action'], $this->_actions)) {
                     /* Create filter if using AND. */
                     if ($filter['combine'] == Ingo_Storage::COMBINE_ALL) {
-                        $recipe = new Procmail_Recipe($filter, $this->_params);
+                        $recipe = new Ingo_Script_Procmail_Recipe($filter, $this->_params);
                         if (!$filter['stop']) {
                             $recipe->addFlag('c');
                         }
                         foreach ($filter['conditions'] as $condition) {
                             $recipe->addCondition($condition);
                         }
-                        $this->addItem(new Procmail_Comment($filter['name'], !empty($filter['disable']), true));
+                        $this->addItem(new Ingo_Script_Procmail_Comment($filter['name'], !empty($filter['disable']), true));
                         $this->addItem($recipe);
                     } else {
                         /* Create filter if using OR */
-                        $this->addItem(new Procmail_Comment($filter['name'], !empty($filter['disable']), true));
+                        $this->addItem(new Ingo_Script_Procmail_Comment($filter['name'], !empty($filter['disable']), true));
                         $loop = 0;
                         foreach ($filter['conditions'] as $condition) {
-                            $recipe = new Procmail_Recipe($filter, $this->_params);
+                            $recipe = new Ingo_Script_Procmail_Recipe($filter, $this->_params);
                             if ($loop++) {
                                 $recipe->addFlag('E');
                             }
@@ -205,23 +205,25 @@ class Ingo_Script_Procmail extends Ingo_Script {
      *
      * @param boolean $disable  Disable the blacklist?
      */
-    function generateBlacklist($disable = false)
+    public function generateBlacklist($disable = false)
     {
-        $blacklist = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_BLACKLIST);
+        $blacklist = $GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_BLACKLIST);
         $bl_addr = $blacklist->getBlacklist();
         $bl_folder = $blacklist->getBlacklistFolder();
 
-        $bl_type = (empty($bl_folder)) ? Ingo_Storage::ACTION_DISCARD : Ingo_Storage::ACTION_MOVE;
+        $bl_type = empty($bl_folder)
+            ? Ingo_Storage::ACTION_DISCARD
+            : Ingo_Storage::ACTION_MOVE;
 
         if (!empty($bl_addr)) {
-            $this->addItem(new Procmail_Comment(_("Blacklisted Addresses"), $disable, true));
+            $this->addItem(new Ingo_Script_Procmail_Comment(_("Blacklisted Addresses"), $disable, true));
             $params = array('action-value' => $bl_folder,
                             'action' => $bl_type,
                             'disable' => $disable);
 
             foreach ($bl_addr as $address) {
                 if (!empty($address)) {
-                    $recipe = new Procmail_Recipe($params, $this->_params);
+                    $recipe = new Ingo_Script_Procmail_Recipe($params, $this->_params);
                     $recipe->addCondition(array('field' => 'From', 'value' => $address, 'match' => 'address'));
                     $this->addItem($recipe);
                 }
@@ -235,16 +237,16 @@ class Ingo_Script_Procmail extends Ingo_Script {
      *
      * @param boolean $disable  Disable the whitelist?
      */
-    function generateWhitelist($disable = false)
+    public function generateWhitelist($disable = false)
     {
-        $whitelist = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_WHITELIST);
+        $whitelist = $GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_WHITELIST);
         $wl_addr = $whitelist->getWhitelist();
 
         if (!empty($wl_addr)) {
-            $this->addItem(new Procmail_Comment(_("Whitelisted Addresses"), $disable, true));
+            $this->addItem(new Ingo_Script_Procmail_Comment(_("Whitelisted Addresses"), $disable, true));
             foreach ($wl_addr as $address) {
                 if (!empty($address)) {
-                    $recipe = new Procmail_Recipe(array('action' => Ingo_Storage::ACTION_KEEP, 'disable' => $disable), $this->_params);
+                    $recipe = new Ingo_Script_Procmail_Recipe(array('action' => Ingo_Storage::ACTION_KEEP, 'disable' => $disable), $this->_params);
                     $recipe->addCondition(array('field' => 'From', 'value' => $address, 'match' => 'address'));
                     $this->addItem($recipe);
                 }
@@ -257,9 +259,9 @@ class Ingo_Script_Procmail extends Ingo_Script {
      *
      * @param boolean $disable  Disable vacation?
      */
-    function generateVacation($disable = false)
+    public function generateVacation($disable = false)
     {
-        $vacation = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_VACATION);
+        $vacation = $GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_VACATION);
         $addresses = $vacation->getVacationAddresses();
         $actionval = array(
             'addresses' => $addresses,
@@ -273,11 +275,11 @@ class Ingo_Script_Procmail extends Ingo_Script {
         );
 
         if (!empty($addresses)) {
-            $this->addItem(new Procmail_Comment(_("Vacation"), $disable, true));
+            $this->addItem(new Ingo_Script_Procmail_Comment(_("Vacation"), $disable, true));
             $params = array('action' => Ingo_Storage::ACTION_VACATION,
                             'action-value' => $actionval,
                             'disable' => $disable);
-            $recipe = new Procmail_Recipe($params, $this->_params);
+            $recipe = new Ingo_Script_Procmail_Recipe($params, $this->_params);
             $this->addItem($recipe);
         }
     }
@@ -287,17 +289,17 @@ class Ingo_Script_Procmail extends Ingo_Script {
      *
      * @param boolean $disable  Disable forwarding?
      */
-    function generateForward($disable = false)
+    public function generateForward($disable = false)
     {
-        $forward = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FORWARD);
+        $forward = $GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FORWARD);
         $addresses = $forward->getForwardAddresses();
 
         if (!empty($addresses)) {
-            $this->addItem(new Procmail_Comment(_("Forwards"), $disable, true));
+            $this->addItem(new Ingo_Script_Procmail_Comment(_("Forwards"), $disable, true));
             $params = array('action' => Ingo_Storage::ACTION_FORWARD,
                             'action-value' => $addresses,
                             'disable' => $disable);
-            $recipe = new Procmail_Recipe($params, $this->_params);
+            $recipe = new Ingo_Script_Procmail_Recipe($params, $this->_params);
             if ($forward->getForwardKeep()) {
                 $recipe->addFlag('c');
             }
@@ -311,490 +313,9 @@ class Ingo_Script_Procmail extends Ingo_Script {
      * @param object $item  The item to add to the recipe list.
      *                      The object should have a generate() function.
      */
-    function addItem($item)
+    public function addItem($item)
     {
         $this->_recipes[] = $item;
     }
 
 }
-
-/**
- * The Procmail_Comment:: class represents a Procmail comment.  This is
- * a pretty simple class, but it makes the code in Ingo_Script_Procmail::
- * cleaner as it provides a generate() function and can be added to the
- * recipe list the same way as a recipe can be.
- *
- * @author  Ben Chavet <ben@chavet.net>
- * @package Ingo
- */
-class Procmail_Comment {
-
-    /**
-     * The comment text.
-     *
-     * @var string
-     */
-    var $_comment = '';
-
-    /**
-     * Constructs a new procmail comment.
-     *
-     * @param string $comment   Comment to be generated.
-     * @param boolean $disable  Output 'DISABLED' comment?
-     * @param boolean $header   Output a 'header' comment?
-     */
-    function Procmail_Comment($comment, $disable = false, $header = false)
-    {
-        if ($disable) {
-            $comment = _("DISABLED: ") . $comment;
-        }
-
-        if ($header) {
-            $this->_comment .= "##### $comment #####";
-        } else {
-            $this->_comment .= "# $comment";
-        }
-    }
-
-    /**
-     * Returns the comment stored by this object.
-     *
-     * @return string  The comment stored by this object.
-     */
-    function generate()
-    {
-        return $this->_comment;
-    }
-
-}
-
-/**
- * The Procmail_Recipe:: class represents a Procmail recipe.
- *
- * @author  Ben Chavet <ben@chavet.net>
- * @package Ingo
- */
-class Procmail_Recipe {
-
-    var $_action = array();
-    var $_conditions = array();
-    var $_disable = '';
-    var $_flags = '';
-    var $_params = array(
-        'date' => 'date',
-        'echo' => 'echo',
-        'ls'   => 'ls'
-    );
-    var $_valid = true;
-
-    /**
-     * Constructs a new procmail recipe.
-     *
-     * @param array $params        Array of parameters.
-     *                               REQUIRED FIELDS:
-     *                                'action'
-     *                               OPTIONAL FIELDS:
-     *                                'action-value' (only used if the
-     *                                'action' requires it)
-     * @param array $scriptparams  Array of parameters passed to
-     *                             Ingo_Script_Procmail::.
-     */
-    function Procmail_Recipe($params = array(), $scriptparams = array())
-    {
-        $this->_disable = !empty($params['disable']);
-        $this->_params = array_merge($this->_params, $scriptparams);
-
-        switch ($params['action']) {
-        case Ingo_Storage::ACTION_KEEP:
-            // Note: you may have to set the DEFAULT variable in your
-            // backend configuration.
-            if (isset($this->_params['delivery_agent']) && isset($this->_params['delivery_mailbox_prefix'])) {
-                $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . '$DEFAULT';
-            } elseif (isset($this->_params['delivery_agent'])) {
-                $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' $DEFAULT';
-            } else {
-                $this->_action[] = '$DEFAULT';
-            }
-            break;
-
-        case Ingo_Storage::ACTION_MOVE:
-            if (isset($this->_params['delivery_agent']) && isset($this->_params['delivery_mailbox_prefix'])) {
-                $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . $this->procmailPath($params['action-value']);
-            } elseif (isset($this->_params['delivery_agent'])) {
-                $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' ' . $this->procmailPath($params['action-value']);
-            } else {
-                $this->_action[] = $this->procmailPath($params['action-value']);
-            }
-            break;
-
-        case Ingo_Storage::ACTION_DISCARD:
-            $this->_action[] = '/dev/null';
-            break;
-
-        case Ingo_Storage::ACTION_REDIRECT:
-            $this->_action[] = '! ' . $params['action-value'];
-            break;
-
-        case Ingo_Storage::ACTION_REDIRECTKEEP:
-            $this->_action[] = '{';
-            $this->_action[] = '  :0 c';
-            $this->_action[] = '  ! ' . $params['action-value'];
-            $this->_action[] = '';
-            $this->_action[] = '  :0' . (isset($this->_params['delivery_agent']) ? ' w' : '');
-            if (isset($this->_params['delivery_agent']) && isset($this->_params['delivery_mailbox_prefix'])) {
-                $this->_action[] = '  | ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . '$DEFAULT';
-            } elseif (isset($this->_params['delivery_agent'])) {
-                $this->_action[] = '  | ' . $this->_params['delivery_agent'] . ' $DEFAULT';
-            } else {
-                $this->_action[] = '  $DEFAULT';
-            }
-            $this->_action[] = '}';
-            break;
-
-        case Ingo_Storage::ACTION_REJECT:
-            $this->_action[] = '{';
-            $this->_action[] = '  EXITCODE=' . $params['action-value'];
-            $this->_action[] = '  HOST="no.address.here"';
-            $this->_action[] = '}';
-            break;
-
-        case Ingo_Storage::ACTION_VACATION:
-            $days = $params['action-value']['days'];
-            $timed = !empty($params['action-value']['start']) &&
-                !empty($params['action-value']['end']);
-            $this->_action[] = '{';
-            foreach ($params['action-value']['addresses'] as $address) {
-                if (!empty($address)) {
-                    $this->_action[] = '  :0';
-                    $this->_action[] = '  * ^TO_' . $address;
-                    $this->_action[] = '  {';
-                    $this->_action[] = '    FILEDATE=`test -f ${VACATION_DIR:-.}/\'.vacation.' . $address . '\' && '
-                        . $this->_params['ls'] . ' -lcn --time-style=+%s ${VACATION_DIR:-.}/\'.vacation.' . $address . '\' | '
-                        . 'awk \'{ print $6 + (' . $days * 86400 . ') }\'`';
-                    $this->_action[] = '    DATE=`' . $this->_params['date'] . ' +%s`';
-                    $this->_action[] = '    DUMMY=`test -f ${VACATION_DIR:-.}/\'.vacation.' . $address . '\' && '
-                        . 'test $FILEDATE -le $DATE && '
-                        . 'rm ${VACATION_DIR:-.}/\'.vacation.' . $address . '\'`';
-                    if ($timed) {
-                        $this->_action[] = '    START=' . $params['action-value']['start'];
-                        $this->_action[] = '    END=' . $params['action-value']['end'];
-                    }
-                    $this->_action[] = '';
-                    $this->_action[] = '    :0 h';
-                    $this->_action[] = '    SUBJECT=| formail -xSubject:';
-                    $this->_action[] = '';
-                    $this->_action[] = '    :0 Whc: ${VACATION_DIR:-.}/vacation.lock';
-                    if ($timed) {
-                        $this->_action[] = '    * ? test $DATE -gt $START && test $END -gt $DATE';
-                       $this->_action[] = '    {';
-                        $this->_action[] = '      :0 Wh';
-                    }
-                    $this->_action[] = '      * ^TO_' . $address;
-                    $this->_action[] = '      * !^X-Loop: ' . $address;
-                    $this->_action[] = '      * !^X-Spam-Flag: YES';
-                    if (count($params['action-value']['excludes']) > 0) {
-                        foreach ($params['action-value']['excludes'] as $exclude) {
-                            if (!empty($exclude)) {
-                                $this->_action[] = '      * !^From.*' . $exclude;
-                            }
-                        }
-                    }
-                    if ($params['action-value']['ignorelist']) {
-                        $this->_action[] = '      * !^FROM_DAEMON';
-                    }
-                    $this->_action[] = '      | formail -rD 8192 ${VACATION_DIR:-.}/.vacation.' . $address;
-                    $this->_action[] = '      :0 eh';
-                    $this->_action[] = '      | (formail -rI"Precedence: junk" \\';
-                    $this->_action[] = '       -a"From: <' . $address . '>" \\';
-                    $this->_action[] = '       -A"X-Loop: ' . $address . '" \\';
-                    if (Horde_Mime::is8bit($params['action-value']['reason'])) {
-                        $this->_action[] = '       -i"Subject: ' . Horde_Mime::encode($params['action-value']['subject'] . ' (Re: $SUBJECT)', $scriptparams['charset']) . '" \\';
-                        $this->_action[] = '       -i"Content-Transfer-Encoding: quoted-printable" \\';
-                        $this->_action[] = '       -i"Content-Type: text/plain; charset=' . $scriptparams['charset'] . '" ; \\';
-                        $reason = Horde_Mime::quotedPrintableEncode($params['action-value']['reason'], "\n");
-                    } else {
-                        $this->_action[] = '       -i"Subject: ' . Horde_Mime::encode($params['action-value']['subject'] . ' (Re: $SUBJECT)', $scriptparams['charset']) . '" ; \\';
-                        $reason = $params['action-value']['reason'];
-                    }
-                    $reason = addcslashes($reason, "\\\n\r\t\"`");
-                    $this->_action[] = '       ' . $this->_params['echo'] . ' -e "' . $reason . '" \\';
-                    $this->_action[] = '      ) | $SENDMAIL -f' . $address . ' -oi -t';
-                    if ($timed) {
-                        $this->_action[] = '    }';
-                    }
-                    $this->_action[] = '  }';
-                }
-            }
-            $this->_action[] = '}';
-            break;
-
-        case Ingo_Storage::ACTION_FORWARD:
-            /* Make sure that we prevent mail loops using 3 methods.
-             *
-             * First, we call sendmail -f to set the envelope sender to be the
-             * same as the original sender, so bounces will go to the original
-             * sender rather than to us.  This unfortunately triggers lots of
-             * Authentication-Warning: messages in sendmail's logs.
-             *
-             * Second, add an X-Loop header, to handle the case where the
-             * address we forward to forwards back to us.
-             *
-             * Third, don't forward mailer daemon messages (i.e., bounces).
-             * Method 1 above should make this redundant, unless we're sending
-             * mail from this account and have a bad forward-to account.
-             *
-             * Get the from address, saving a call to formail if possible.
-             * The procmail code for doing this is borrowed from the
-             * Procmail Library Project, http://pm-lib.sourceforge.net/.
-             * The Ingo project has the permission to use Procmail Library code
-             * under Apache licence v 1.x or any later version.
-             * Permission obtained 2006-04-04 from Author Jari Aalto. */
-            $this->_action[] = '{';
-            $this->_action[] = '  :0 ';
-            $this->_action[] = '  *$ ! ^From *\/[^  ]+';
-            $this->_action[] = '  *$ ! ^Sender: *\/[^   ]+';
-            $this->_action[] = '  *$ ! ^From: *\/[^     ]+';
-            $this->_action[] = '  *$ ! ^Reply-to: *\/[^     ]+';
-            $this->_action[] = '  {';
-            $this->_action[] = '    OUTPUT = `formail -zxFrom:`';
-            $this->_action[] = '  }';
-            $this->_action[] = '  :0 E';
-            $this->_action[] = '  {';
-            $this->_action[] = '    OUTPUT = $MATCH';
-            $this->_action[] = '  }';
-            $this->_action[] = '';
-
-            /* Forward to each address on our list. */
-            foreach ($params['action-value'] as $address) {
-                if (!empty($address)) {
-                    $this->_action[] = '  :0 c';
-                    $this->_action[] = '  * !^FROM_MAILER';
-                    $this->_action[] = '  * !^X-Loop: to-' . $address;
-                    $this->_action[] = '  | formail -A"X-Loop: to-' . $address . '" | $SENDMAIL -oi -f $OUTPUT ' . $address;
-                }
-            }
-
-            /* In case of mail loop or bounce, store a copy locally.  Note
-             * that if we forward to more than one address, only a mail loop
-             * on the last address will cause a local copy to be saved.  TODO:
-             * The next two lines are redundant (and create an extra copy of
-             * the message) if "Keep a copy of messages in this account" is
-             * checked. */
-            $this->_action[] = '  :0 E' . (isset($this->_params['delivery_agent']) ? 'w' : '');
-            if (isset($this->_params['delivery_agent'])) {
-                $this->_action[] = isset($this->_params['delivery_mailbox_prefix']) ?
-                    ' | ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . '$DEFAULT' :
-                    ' | ' . $this->_params['delivery_agent'] . ' $DEFAULT';
-            } else {
-                $this->_action[] = '  $DEFAULT';
-            }
-            $this->_action[] = '  :0 ';
-            $this->_action[] = '  /dev/null';
-            $this->_action[] = '}';
-            break;
-
-        default:
-            $this->_valid = false;
-            break;
-        }
-    }
-
-    /**
-     * Adds a flag to the recipe.
-     *
-     * @param string $flag  String of flags to append to the current flags.
-     */
-    function addFlag($flag)
-    {
-        $this->_flags .= $flag;
-    }
-
-    /**
-     * Adds a condition to the recipe.
-     *
-     * @param array $condition  Array of parameters. Required keys are 'field'
-     *                          and 'value'. 'case' is an optional key.
-     */
-    function addCondition($condition = array())
-    {
-        $flag = !empty($condition['case']) ? 'D' : '';
-        $match = isset($condition['match']) ? $condition['match'] : null;
-        $string = '';
-        $prefix = '';
-
-        switch ($condition['field']) {
-        case 'Destination':
-            $string = '^TO_';
-            break;
-
-        case 'Body':
-            $flag .= 'B';
-            break;
-
-        default:
-            // convert 'field' to PCRE pattern matching
-            if (!strpos($condition['field'], ',')) {
-                $string = '^' . $condition['field'] . ':';
-            } else {
-                $string .= '^(' . str_replace(',', '|', $condition['field']) . '):';
-            }
-            $prefix = ' ';
-        }
-
-        $reverseCondition = false;
-        switch ($match) {
-        case 'regex':
-            $string .= $prefix . $condition['value'];
-            break;
-
-        case 'address':
-            $string .= '(.*\<)?' . quotemeta($condition['value']);
-            break;
-
-        case 'not begins with':
-            $reverseCondition = true;
-            // fall through
-        case 'begins with':
-            $string .= $prefix . quotemeta($condition['value']);
-            break;
-
-        case 'not ends with':
-            $reverseCondition = true;
-            // fall through
-        case 'ends with':
-            $string .= '.*' . quotemeta($condition['value']) . '$';
-            break;
-
-        case 'not contain':
-            $reverseCondition = true;
-            // fall through
-        case 'contains':
-        default:
-            $string .= '.*' . quotemeta($condition['value']);
-            break;
-        }
-
-        $this->_conditions[] = array('condition' => ($reverseCondition ? '* !' : '* ') . $string,
-                                     'flags' => $flag);
-    }
-
-    /**
-     * Generates procmail code to represent the recipe.
-     *
-     * @return string  Procmail code to represent the recipe.
-     */
-    function generate()
-    {
-        $nest = 0;
-        $prefix = '';
-        $text = array();
-
-        if (!$this->_valid) {
-            return '';
-        }
-
-        // Set the global flags for the whole rule, each condition
-        // will add its own (such as Body or Case Sensitive)
-        $global = $this->_flags;
-        if (isset($this->_conditions[0])) {
-            $global .= $this->_conditions[0]['flags'];
-        }
-        $text[] = ':0 ' . $global . (isset($this->_params['delivery_agent']) ? 'w' : '');
-        foreach ($this->_conditions as $condition) {
-            if ($nest > 0) {
-                $text[] = str_repeat('  ', $nest - 1) . '{';
-                $text[] = str_repeat('  ', $nest) . ':0 ' . $condition['flags'];
-                $text[] = str_repeat('  ', $nest) . $condition['condition'];
-            } else {
-                $text[] = $condition['condition'];
-            }
-            $nest++;
-        }
-
-        if (--$nest > 0) {
-            $prefix = str_repeat('  ', $nest);
-        }
-        foreach ($this->_action as $val) {
-            $text[] = $prefix . $val;
-        }
-
-        for ($i = $nest; $i > 0; $i--) {
-            $text[] = str_repeat('  ', $i - 1) . '}';
-        }
-
-        if ($this->_disable) {
-            $code = '';
-            foreach ($text as $val) {
-                $comment = new Procmail_Comment($val);
-                $code .= $comment->generate() . "\n";
-            }
-            return $code . "\n";
-        } else {
-            return implode("\n", $text) . "\n";
-        }
-    }
-
-    /**
-     * Returns a procmail-ready mailbox path, converting IMAP folder
-     * pathname conventions as necessary.
-     *
-     * @param string $folder  The IMAP folder name.
-     *
-     * @return string  The procmail mailbox path.
-     */
-    function procmailPath($folder)
-    {
-        /* NOTE: '$DEFAULT' here is a literal, not a PHP variable. */
-        if (isset($this->_params) &&
-            ($this->_params['path_style'] == 'maildir')) {
-            if (empty($folder) || ($folder == 'INBOX')) {
-                return '$DEFAULT';
-            }
-            if (substr($folder, 0, 6) == 'INBOX.') {
-                $folder = substr($folder, 6);
-            }
-            return '"$DEFAULT/.' . escapeshellcmd($folder) . '/"';
-        } else {
-            if (empty($folder) || ($folder == 'INBOX')) {
-                return '$DEFAULT';
-            }
-            return str_replace(' ', '\ ', escapeshellcmd($folder));
-        }
-    }
-
-}
-
-/**
- * The Procmail_Variable:: class represents a Procmail variable.
- *
- * @author  Michael Slusarz <slusarz@horde.org>
- * @package Ingo
- */
-class Procmail_Variable {
-
-    var $_name;
-    var $_value;
-
-    /**
-     * Constructs a new procmail variable.
-     *
-     * @param array $params  Array of parameters. Expected fields are 'name'
-     *                       and 'value'.
-     */
-    function Procmail_Variable($params = array())
-    {
-        $this->_name = $params['name'];
-        $this->_value = $params['value'];
-    }
-
-    /**
-     * Generates procmail code to represent the variable.
-     *
-     * @return string  Procmail code to represent the variable.
-     */
-    function generate()
-    {
-        return $this->_name . '=' . $this->_value . "\n";
-    }
-
-}
diff --git a/ingo/lib/Script/Procmail/Comment.php b/ingo/lib/Script/Procmail/Comment.php
new file mode 100644 (file)
index 0000000..d31a4a6
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+/**
+ * The Ingo_Script_Procmail_Comment:: class represents a Procmail comment.
+ * This is a simple class, but it makes the code in Ingo_Script_Procmail::
+ * cleaner as it provides a generate() function and can be added to the
+ * recipe list the same way as a recipe can be.
+ *
+ * Copyright 2003-2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Ben Chavet <ben@horde.org>
+ * @package Ingo
+ */
+class Ingo_Script_Procmail_Comment
+{
+    /**
+     * The comment text.
+     *
+     * @var string
+     */
+    protected $_comment = '';
+
+    /**
+     * Constructs a new procmail comment.
+     *
+     * @param string $comment   Comment to be generated.
+     * @param boolean $disable  Output 'DISABLED' comment?
+     * @param boolean $header   Output a 'header' comment?
+     */
+    public function __construct($comment, $disable = false, $header = false)
+    {
+        if ($disable) {
+            $comment = _("DISABLED: ") . $comment;
+        }
+
+        $this->_comment = $header
+            ? '##### ' . $comment  . ' #####'
+            : '# ' . $comment;
+    }
+
+    /**
+     * Returns the comment stored by this object.
+     *
+     * @return string  The comment stored by this object.
+     */
+    public function generate()
+    {
+        return $this->_comment;
+    }
+
+}
diff --git a/ingo/lib/Script/Procmail/Recipe.php b/ingo/lib/Script/Procmail/Recipe.php
new file mode 100644 (file)
index 0000000..50fe06a
--- /dev/null
@@ -0,0 +1,418 @@
+<?php
+/**
+ * The Ingo_Script_Procmail_Recipe:: class represents a Procmail recipe.
+ *
+ * Copyright 2003-2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Ben Chavet <ben@horde.org>
+ * @package Ingo
+ */
+class Ingo_Script_Procmail_Recipe
+{
+    /**
+     */
+    protected $_action = array();
+
+    /**
+     */
+    protected $_conditions = array();
+
+    /**
+     */
+    protected $_disable = '';
+
+    /**
+     */
+    protected $_flags = '';
+
+    /**
+     */
+    protected $_params = array(
+        'date' => 'date',
+        'echo' => 'echo',
+        'ls'   => 'ls'
+    );
+
+    /**
+     */
+    protected $_valid = true;
+
+    /**
+     * Constructs a new procmail recipe.
+     *
+     * @param array $params        Array of parameters.
+     *                               REQUIRED FIELDS:
+     *                                'action'
+     *                               OPTIONAL FIELDS:
+     *                                'action-value' (only used if the
+     *                                'action' requires it)
+     * @param array $scriptparams  Array of parameters passed to
+     *                             Ingo_Script_Procmail::.
+     */
+    public function __construct($params = array(), $scriptparams = array())
+    {
+        $this->_disable = !empty($params['disable']);
+        $this->_params = array_merge($this->_params, $scriptparams);
+
+        switch ($params['action']) {
+        case Ingo_Storage::ACTION_KEEP:
+            // Note: you may have to set the DEFAULT variable in your
+            // backend configuration.
+            if (isset($this->_params['delivery_agent']) && isset($this->_params['delivery_mailbox_prefix'])) {
+                $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . '$DEFAULT';
+            } elseif (isset($this->_params['delivery_agent'])) {
+                $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' $DEFAULT';
+            } else {
+                $this->_action[] = '$DEFAULT';
+            }
+            break;
+
+        case Ingo_Storage::ACTION_MOVE:
+            if (isset($this->_params['delivery_agent']) && isset($this->_params['delivery_mailbox_prefix'])) {
+                $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . $this->procmailPath($params['action-value']);
+            } elseif (isset($this->_params['delivery_agent'])) {
+                $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' ' . $this->procmailPath($params['action-value']);
+            } else {
+                $this->_action[] = $this->procmailPath($params['action-value']);
+            }
+            break;
+
+        case Ingo_Storage::ACTION_DISCARD:
+            $this->_action[] = '/dev/null';
+            break;
+
+        case Ingo_Storage::ACTION_REDIRECT:
+            $this->_action[] = '! ' . $params['action-value'];
+            break;
+
+        case Ingo_Storage::ACTION_REDIRECTKEEP:
+            $this->_action[] = '{';
+            $this->_action[] = '  :0 c';
+            $this->_action[] = '  ! ' . $params['action-value'];
+            $this->_action[] = '';
+            $this->_action[] = '  :0' . (isset($this->_params['delivery_agent']) ? ' w' : '');
+            if (isset($this->_params['delivery_agent']) && isset($this->_params['delivery_mailbox_prefix'])) {
+                $this->_action[] = '  | ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . '$DEFAULT';
+            } elseif (isset($this->_params['delivery_agent'])) {
+                $this->_action[] = '  | ' . $this->_params['delivery_agent'] . ' $DEFAULT';
+            } else {
+                $this->_action[] = '  $DEFAULT';
+            }
+            $this->_action[] = '}';
+            break;
+
+        case Ingo_Storage::ACTION_REJECT:
+            $this->_action[] = '{';
+            $this->_action[] = '  EXITCODE=' . $params['action-value'];
+            $this->_action[] = '  HOST="no.address.here"';
+            $this->_action[] = '}';
+            break;
+
+        case Ingo_Storage::ACTION_VACATION:
+            $days = $params['action-value']['days'];
+            $timed = !empty($params['action-value']['start']) &&
+                !empty($params['action-value']['end']);
+            $this->_action[] = '{';
+            foreach ($params['action-value']['addresses'] as $address) {
+                if (!empty($address)) {
+                    $this->_action[] = '  :0';
+                    $this->_action[] = '  * ^TO_' . $address;
+                    $this->_action[] = '  {';
+                    $this->_action[] = '    FILEDATE=`test -f ${VACATION_DIR:-.}/\'.vacation.' . $address . '\' && '
+                        . $this->_params['ls'] . ' -lcn --time-style=+%s ${VACATION_DIR:-.}/\'.vacation.' . $address . '\' | '
+                        . 'awk \'{ print $6 + (' . $days * 86400 . ') }\'`';
+                    $this->_action[] = '    DATE=`' . $this->_params['date'] . ' +%s`';
+                    $this->_action[] = '    DUMMY=`test -f ${VACATION_DIR:-.}/\'.vacation.' . $address . '\' && '
+                        . 'test $FILEDATE -le $DATE && '
+                        . 'rm ${VACATION_DIR:-.}/\'.vacation.' . $address . '\'`';
+                    if ($timed) {
+                        $this->_action[] = '    START=' . $params['action-value']['start'];
+                        $this->_action[] = '    END=' . $params['action-value']['end'];
+                    }
+                    $this->_action[] = '';
+                    $this->_action[] = '    :0 h';
+                    $this->_action[] = '    SUBJECT=| formail -xSubject:';
+                    $this->_action[] = '';
+                    $this->_action[] = '    :0 Whc: ${VACATION_DIR:-.}/vacation.lock';
+                    if ($timed) {
+                        $this->_action[] = '    * ? test $DATE -gt $START && test $END -gt $DATE';
+                       $this->_action[] = '    {';
+                        $this->_action[] = '      :0 Wh';
+                    }
+                    $this->_action[] = '      * ^TO_' . $address;
+                    $this->_action[] = '      * !^X-Loop: ' . $address;
+                    $this->_action[] = '      * !^X-Spam-Flag: YES';
+                    if (count($params['action-value']['excludes']) > 0) {
+                        foreach ($params['action-value']['excludes'] as $exclude) {
+                            if (!empty($exclude)) {
+                                $this->_action[] = '      * !^From.*' . $exclude;
+                            }
+                        }
+                    }
+                    if ($params['action-value']['ignorelist']) {
+                        $this->_action[] = '      * !^FROM_DAEMON';
+                    }
+                    $this->_action[] = '      | formail -rD 8192 ${VACATION_DIR:-.}/.vacation.' . $address;
+                    $this->_action[] = '      :0 eh';
+                    $this->_action[] = '      | (formail -rI"Precedence: junk" \\';
+                    $this->_action[] = '       -a"From: <' . $address . '>" \\';
+                    $this->_action[] = '       -A"X-Loop: ' . $address . '" \\';
+                    if (Horde_Mime::is8bit($params['action-value']['reason'])) {
+                        $this->_action[] = '       -i"Subject: ' . Horde_Mime::encode($params['action-value']['subject'] . ' (Re: $SUBJECT)', $scriptparams['charset']) . '" \\';
+                        $this->_action[] = '       -i"Content-Transfer-Encoding: quoted-printable" \\';
+                        $this->_action[] = '       -i"Content-Type: text/plain; charset=' . $scriptparams['charset'] . '" ; \\';
+                        $reason = Horde_Mime::quotedPrintableEncode($params['action-value']['reason'], "\n");
+                    } else {
+                        $this->_action[] = '       -i"Subject: ' . Horde_Mime::encode($params['action-value']['subject'] . ' (Re: $SUBJECT)', $scriptparams['charset']) . '" ; \\';
+                        $reason = $params['action-value']['reason'];
+                    }
+                    $reason = addcslashes($reason, "\\\n\r\t\"`");
+                    $this->_action[] = '       ' . $this->_params['echo'] . ' -e "' . $reason . '" \\';
+                    $this->_action[] = '      ) | $SENDMAIL -f' . $address . ' -oi -t';
+                    if ($timed) {
+                        $this->_action[] = '    }';
+                    }
+                    $this->_action[] = '  }';
+                }
+            }
+            $this->_action[] = '}';
+            break;
+
+        case Ingo_Storage::ACTION_FORWARD:
+            /* Make sure that we prevent mail loops using 3 methods.
+             *
+             * First, we call sendmail -f to set the envelope sender to be the
+             * same as the original sender, so bounces will go to the original
+             * sender rather than to us.  This unfortunately triggers lots of
+             * Authentication-Warning: messages in sendmail's logs.
+             *
+             * Second, add an X-Loop header, to handle the case where the
+             * address we forward to forwards back to us.
+             *
+             * Third, don't forward mailer daemon messages (i.e., bounces).
+             * Method 1 above should make this redundant, unless we're sending
+             * mail from this account and have a bad forward-to account.
+             *
+             * Get the from address, saving a call to formail if possible.
+             * The procmail code for doing this is borrowed from the
+             * Procmail Library Project, http://pm-lib.sourceforge.net/.
+             * The Ingo project has the permission to use Procmail Library code
+             * under Apache licence v 1.x or any later version.
+             * Permission obtained 2006-04-04 from Author Jari Aalto. */
+            $this->_action[] = '{';
+            $this->_action[] = '  :0 ';
+            $this->_action[] = '  *$ ! ^From *\/[^  ]+';
+            $this->_action[] = '  *$ ! ^Sender: *\/[^   ]+';
+            $this->_action[] = '  *$ ! ^From: *\/[^     ]+';
+            $this->_action[] = '  *$ ! ^Reply-to: *\/[^     ]+';
+            $this->_action[] = '  {';
+            $this->_action[] = '    OUTPUT = `formail -zxFrom:`';
+            $this->_action[] = '  }';
+            $this->_action[] = '  :0 E';
+            $this->_action[] = '  {';
+            $this->_action[] = '    OUTPUT = $MATCH';
+            $this->_action[] = '  }';
+            $this->_action[] = '';
+
+            /* Forward to each address on our list. */
+            foreach ($params['action-value'] as $address) {
+                if (!empty($address)) {
+                    $this->_action[] = '  :0 c';
+                    $this->_action[] = '  * !^FROM_MAILER';
+                    $this->_action[] = '  * !^X-Loop: to-' . $address;
+                    $this->_action[] = '  | formail -A"X-Loop: to-' . $address . '" | $SENDMAIL -oi -f $OUTPUT ' . $address;
+                }
+            }
+
+            /* In case of mail loop or bounce, store a copy locally.  Note
+             * that if we forward to more than one address, only a mail loop
+             * on the last address will cause a local copy to be saved.  TODO:
+             * The next two lines are redundant (and create an extra copy of
+             * the message) if "Keep a copy of messages in this account" is
+             * checked. */
+            $this->_action[] = '  :0 E' . (isset($this->_params['delivery_agent']) ? 'w' : '');
+            if (isset($this->_params['delivery_agent'])) {
+                $this->_action[] = isset($this->_params['delivery_mailbox_prefix']) ?
+                    ' | ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . '$DEFAULT' :
+                    ' | ' . $this->_params['delivery_agent'] . ' $DEFAULT';
+            } else {
+                $this->_action[] = '  $DEFAULT';
+            }
+            $this->_action[] = '  :0 ';
+            $this->_action[] = '  /dev/null';
+            $this->_action[] = '}';
+            break;
+
+        default:
+            $this->_valid = false;
+            break;
+        }
+    }
+
+    /**
+     * Adds a flag to the recipe.
+     *
+     * @param string $flag  String of flags to append to the current flags.
+     */
+    public function addFlag($flag)
+    {
+        $this->_flags .= $flag;
+    }
+
+    /**
+     * Adds a condition to the recipe.
+     *
+     * @param array $condition  Array of parameters. Required keys are 'field'
+     *                          and 'value'. 'case' is an optional key.
+     */
+    public function addCondition($condition = array())
+    {
+        $flag = !empty($condition['case']) ? 'D' : '';
+        $match = isset($condition['match']) ? $condition['match'] : null;
+        $string = '';
+        $prefix = '';
+
+        switch ($condition['field']) {
+        case 'Destination':
+            $string = '^TO_';
+            break;
+
+        case 'Body':
+            $flag .= 'B';
+            break;
+
+        default:
+            // convert 'field' to PCRE pattern matching
+            if (!strpos($condition['field'], ',')) {
+                $string = '^' . $condition['field'] . ':';
+            } else {
+                $string .= '^(' . str_replace(',', '|', $condition['field']) . '):';
+            }
+            $prefix = ' ';
+        }
+
+        $reverseCondition = false;
+        switch ($match) {
+        case 'regex':
+            $string .= $prefix . $condition['value'];
+            break;
+
+        case 'address':
+            $string .= '(.*\<)?' . quotemeta($condition['value']);
+            break;
+
+        case 'not begins with':
+            $reverseCondition = true;
+            // fall through
+        case 'begins with':
+            $string .= $prefix . quotemeta($condition['value']);
+            break;
+
+        case 'not ends with':
+            $reverseCondition = true;
+            // fall through
+        case 'ends with':
+            $string .= '.*' . quotemeta($condition['value']) . '$';
+            break;
+
+        case 'not contain':
+            $reverseCondition = true;
+            // fall through
+        case 'contains':
+        default:
+            $string .= '.*' . quotemeta($condition['value']);
+            break;
+        }
+
+        $this->_conditions[] = array('condition' => ($reverseCondition ? '* !' : '* ') . $string,
+                                     'flags' => $flag);
+    }
+
+    /**
+     * Generates procmail code to represent the recipe.
+     *
+     * @return string  Procmail code to represent the recipe.
+     */
+    public function generate()
+    {
+        $nest = 0;
+        $prefix = '';
+        $text = array();
+
+        if (!$this->_valid) {
+            return '';
+        }
+
+        // Set the global flags for the whole rule, each condition
+        // will add its own (such as Body or Case Sensitive)
+        $global = $this->_flags;
+        if (isset($this->_conditions[0])) {
+            $global .= $this->_conditions[0]['flags'];
+        }
+        $text[] = ':0 ' . $global . (isset($this->_params['delivery_agent']) ? 'w' : '');
+        foreach ($this->_conditions as $condition) {
+            if ($nest > 0) {
+                $text[] = str_repeat('  ', $nest - 1) . '{';
+                $text[] = str_repeat('  ', $nest) . ':0 ' . $condition['flags'];
+                $text[] = str_repeat('  ', $nest) . $condition['condition'];
+            } else {
+                $text[] = $condition['condition'];
+            }
+            $nest++;
+        }
+
+        if (--$nest > 0) {
+            $prefix = str_repeat('  ', $nest);
+        }
+        foreach ($this->_action as $val) {
+            $text[] = $prefix . $val;
+        }
+
+        for ($i = $nest; $i > 0; $i--) {
+            $text[] = str_repeat('  ', $i - 1) . '}';
+        }
+
+        if ($this->_disable) {
+            $code = '';
+            foreach ($text as $val) {
+                $comment = new Ingo_Script_Procmail_Comment($val);
+                $code .= $comment->generate() . "\n";
+            }
+            return $code . "\n";
+        } else {
+            return implode("\n", $text) . "\n";
+        }
+    }
+
+    /**
+     * Returns a procmail-ready mailbox path, converting IMAP folder
+     * pathname conventions as necessary.
+     *
+     * @param string $folder  The IMAP folder name.
+     *
+     * @return string  The procmail mailbox path.
+     */
+    public function procmailPath($folder)
+    {
+        /* NOTE: '$DEFAULT' here is a literal, not a PHP variable. */
+        if (isset($this->_params) &&
+            ($this->_params['path_style'] == 'maildir')) {
+            if (empty($folder) || ($folder == 'INBOX')) {
+                return '$DEFAULT';
+            }
+            if (substr($folder, 0, 6) == 'INBOX.') {
+                $folder = substr($folder, 6);
+            }
+            return '"$DEFAULT/.' . escapeshellcmd($folder) . '/"';
+        } else {
+            if (empty($folder) || ($folder == 'INBOX')) {
+                return '$DEFAULT';
+            }
+            return str_replace(' ', '\ ', escapeshellcmd($folder));
+        }
+    }
+
+}
diff --git a/ingo/lib/Script/Procmail/Variable.php b/ingo/lib/Script/Procmail/Variable.php
new file mode 100644 (file)
index 0000000..5fd4ad7
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+/**
+ * The Ingo_Script_Procmail_Variable:: class represents a Procmail variable.
+ *
+ * Copyright 2003-2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Michael Slusarz <slusarz@horde.org>
+ * @package Ingo
+ */
+class Ingo_Script_Procmail_Variable
+{
+    /**
+     */
+    protected $_name;
+
+    /**
+     */
+    protected $_value;
+
+    /**
+     * Constructs a new procmail variable.
+     *
+     * @param array $params  Array of parameters. Expected fields are 'name'
+     *                       and 'value'.
+     */
+    public function __construct($params = array())
+    {
+        $this->_name = $params['name'];
+        $this->_value = $params['value'];
+    }
+
+    /**
+     * Generates procmail code to represent the variable.
+     *
+     * @return string  Procmail code to represent the variable.
+     */
+    public function generate()
+    {
+        return $this->_name . '=' . $this->_value . "\n";
+    }
+
+}
index 5ea6daf..4b2bfe4 100644 (file)
@@ -8,14 +8,14 @@
  * @author  Mike Cochrane <mike@graftonhall.co.nz>
  * @package Ingo
  */
-class Ingo_Script_Sieve extends Ingo_Script {
-
+class Ingo_Script_Sieve extends Ingo_Script
+{
     /**
      * The list of actions allowed (implemented) for this driver.
      *
      * @var array
      */
-    var $_actions = array(
+    protected $_actions = array(
         Ingo_Storage::ACTION_KEEP,
         Ingo_Storage::ACTION_MOVE,
         Ingo_Storage::ACTION_DISCARD,
@@ -32,7 +32,7 @@ class Ingo_Script_Sieve extends Ingo_Script {
      *
      * @var array
      */
-    var $_categories = array(
+    protected $_categories = array(
         Ingo_Storage::ACTION_BLACKLIST,
         Ingo_Storage::ACTION_WHITELIST,
         Ingo_Storage::ACTION_VACATION,
@@ -45,11 +45,25 @@ class Ingo_Script_Sieve extends Ingo_Script {
      *
      * @var array
      */
-    var $_tests = array(
-        'contains', 'not contain', 'is', 'not is', 'begins with',
-        'not begins with', 'ends with', 'not ends with', 'exists', 'not exist',
-        'less than', 'less than or equal to', 'equal', 'not equal',
-        'greater than', 'greater than or equal to', 'regex', 'matches',
+    protected $_tests = array(
+        'contains',
+        'not contain',
+        'is',
+        'not is',
+        'begins with',
+        'not begins with',
+        'ends with',
+        'not ends with',
+        'exists',
+        'not exist',
+        'less than',
+        'less than or equal to',
+        'equal',
+        'not equal',
+        'greater than',
+        'greater than or equal to',
+        'regex',
+        'matches',
         'not matches'
     );
 
@@ -58,7 +72,7 @@ class Ingo_Script_Sieve extends Ingo_Script {
      *
      * @var array
      */
-    var $_types = array(
+    protected $_types = array(
         Ingo_Storage::TYPE_HEADER,
         Ingo_Storage::TYPE_SIZE,
         Ingo_Storage::TYPE_BODY
@@ -69,49 +83,49 @@ class Ingo_Script_Sieve extends Ingo_Script {
      *
      * @var boolean
      */
-    var $_casesensitive = true;
+    protected $_casesensitive = true;
 
     /**
      * Does the driver support setting IMAP flags?
      *
      * @var boolean
      */
-    var $_supportIMAPFlags = true;
+    protected $_supportIMAPFlags = true;
 
     /**
      * Does the driver support the stop-script option?
      *
      * @var boolean
      */
-    var $_supportStopScript = true;
+    protected $_supportStopScript = true;
 
     /**
      * Does the driver require a script file to be generated?
      *
      * @var boolean
      */
-    var $_scriptfile = true;
+    protected $_scriptfile = true;
 
     /**
      * The blocks that make up the code.
      *
      * @var array
      */
-    var $_blocks = array();
+    protected $_blocks = array();
 
     /**
      * The blocks that have to appear at the end of the code.
      *
      * @var array
      */
-    var $_endBlocks = array();
+    protected $_endBlocks = array();
 
     /**
      * Returns a script previously generated with generate().
      *
      * @return string  The Sieve script.
      */
-    function toCode()
+    public function toCode()
     {
         $code = "# Sieve Filter\n# "
             . _("Generated by Ingo (http://www.horde.org/ingo/)") . ' ('
@@ -147,7 +161,7 @@ class Ingo_Script_Sieve extends Ingo_Script {
      *
      * @return string  The string, UTF-8 encoded.
      */
-    function encode($string)
+    public function encode($string)
     {
         return Horde_String::convertCharset($string, $this->_params['charset'], 'UTF-8');
     }
@@ -161,16 +175,14 @@ class Ingo_Script_Sieve extends Ingo_Script {
      *
      * @return string  The escaped string.
      */
-    function escapeString($string, $regexmode = false)
+    static public function escapeString($string, $regexmode = false)
     {
         /* Remove any backslashes in front of commas. */
         $string = str_replace('\,', ',', $string);
 
-        if ($regexmode) {
-            return str_replace('"', addslashes('"'), $string);
-        } else {
-            return str_replace(array('\\', '"'), array(addslashes('\\'), addslashes('"')), $string);
-        }
+        return $regexmode
+            ? str_replace('"', addslashes('"'), $string)
+            : str_replace(array('\\', '"'), array(addslashes('\\'), addslashes('"')), $string);
     }
 
     /**
@@ -179,7 +191,7 @@ class Ingo_Script_Sieve extends Ingo_Script {
      * @return boolean|string  True if all rules are valid, an error message
      *                         otherwise.
      */
-    function check()
+    public function check()
     {
         foreach ($this->_blocks as $block) {
             $res = $block->check();
@@ -197,7 +209,7 @@ class Ingo_Script_Sieve extends Ingo_Script {
      *
      * @return array  A Sieve extension list.
      */
-    function requires()
+    public function requires()
     {
         $requires = array();
         foreach ($this->_blocks as $block) {
@@ -210,13 +222,13 @@ class Ingo_Script_Sieve extends Ingo_Script {
     /**
      * Adds all blocks necessary for the forward rule.
      */
-    function _addForwardBlocks()
+    protected function _addForwardBlocks()
     {
         if (!$this->_validRule(Ingo_Storage::ACTION_FORWARD)) {
             return;
         }
 
-        $forward = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FORWARD);
+        $forward = $GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FORWARD);
         $fwd_addr = $forward->getForwardAddresses();
         if (empty($fwd_addr)) {
             return;
@@ -226,26 +238,26 @@ class Ingo_Script_Sieve extends Ingo_Script {
         foreach ($fwd_addr as $addr) {
             $addr = trim($addr);
             if (!empty($addr)) {
-                $action[] = new Sieve_Action_Redirect(array('address' => $addr));
+                $action[] = new Ingo_Script_Sieve_Action_Redirect(array('address' => $addr));
             }
         }
 
         if (count($action)) {
             if($forward->getForwardKeep()) {
-                $this->_endBlocks[] = new Sieve_Comment($this->encode(_("Forward Keep Action")));
-                $if = new Sieve_If(new Sieve_Test_True());
-                $if->setActions(array(new Sieve_Action_Keep(),
-                                      new Sieve_Action_Stop()));
+                $this->_endBlocks[] = new Ingo_Script_Sieve_Comment($this->encode(_("Forward Keep Action")));
+                $if = new Ingo_Script_Sieve_If(new Ingo_Script_Sieve_Test_True());
+                $if->setActions(array(new Ingo_Script_Sieve_Action_Keep(),
+                                      new Ingo_Script_Sieve_Action_Stop()));
                 $this->_endBlocks[] = $if;
             } else {
-                $action[] = new Sieve_Action_Stop();
+                $action[] = new Ingo_Script_Sieve_Action_Stop();
             }
         }
 
-        $this->_blocks[] = new Sieve_Comment($this->encode(_("Forwards")));
+        $this->_blocks[] = new Ingo_Script_Sieve_Comment($this->encode(_("Forwards")));
 
-        $test = new Sieve_Test_True();
-        $if = new Sieve_If($test);
+        $test = new Ingo_Script_Sieve_Test_True();
+        $if = new Ingo_Script_Sieve_If($test);
         $if->setActions($action);
         $this->_blocks[] = $if;
     }
@@ -253,13 +265,13 @@ class Ingo_Script_Sieve extends Ingo_Script {
     /**
      * Adds all blocks necessary for the blacklist rule.
      */
-    function _addBlacklistBlocks()
+    protected function _addBlacklistBlocks()
     {
         if (!$this->_validRule(Ingo_Storage::ACTION_BLACKLIST)) {
             return;
         }
 
-        $blacklist = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_BLACKLIST);
+        $blacklist = $GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_BLACKLIST);
         $bl_addr = $blacklist->getBlacklist();
         $folder = $blacklist->getBlacklistFolder();
         if (empty($bl_addr)) {
@@ -268,18 +280,18 @@ class Ingo_Script_Sieve extends Ingo_Script {
 
         $action = array();
         if (empty($folder)) {
-            $action[] = new Sieve_Action_Discard();
+            $action[] = new Ingo_Script_Sieve_Action_Discard();
         } elseif ($folder == Ingo::BLACKLIST_MARKER) {
-            $action[] = new Sieve_Action_Addflag(array('flags' => Ingo_Storage::FLAG_DELETED));
-            $action[] = new Sieve_Action_Keep();
-            $action[] = new Sieve_Action_Removeflag(array('flags' => Ingo_Storage::FLAG_DELETED));
+            $action[] = new Ingo_Script_Sieve_Action_Addflag(array('flags' => Ingo_Storage::FLAG_DELETED));
+            $action[] = new Ingo_Script_Sieve_Action_Keep();
+            $action[] = new Ingo_Script_Sieve_Action_Removeflag(array('flags' => Ingo_Storage::FLAG_DELETED));
         } else {
-            $action[] = new Sieve_Action_Fileinto(array_merge($this->_params, array('folder' => $folder)));
+            $action[] = new Ingo_Script_Sieve_Action_Fileinto(array_merge($this->_params, array('folder' => $folder)));
         }
 
-        $action[] = new Sieve_Action_Stop();
+        $action[] = new Ingo_Script_Sieve_Action_Stop();
 
-        $this->_blocks[] = new Sieve_Comment($this->encode(_("Blacklisted Addresses")));
+        $this->_blocks[] = new Ingo_Script_Sieve_Comment($this->encode(_("Blacklisted Addresses")));
 
         /* Split the test up to only do 5 addresses at a time. */
         $temp = array();
@@ -294,15 +306,15 @@ class Ingo_Script_Sieve extends Ingo_Script {
                 }
             }
             if (count($temp) == 5) {
-                $test = new Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'addresses' => implode("\n", $temp)));
-                $if = new Sieve_If($test);
+                $test = new Ingo_Script_Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'addresses' => implode("\n", $temp)));
+                $if = new Ingo_Script_Sieve_If($test);
                 $if->setActions($action);
                 $this->_blocks[] = $if;
                 $temp = array();
             }
             if (count($wildcards) == 5) {
-                $test = new Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'match-type' => ':matches', 'addresses' => implode("\n", $wildcards)));
-                $if = new Sieve_If($test);
+                $test = new Ingo_Script_Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'match-type' => ':matches', 'addresses' => implode("\n", $wildcards)));
+                $if = new Ingo_Script_Sieve_If($test);
                 $if->setActions($action);
                 $this->_blocks[] = $if;
                 $wildcards = array();
@@ -310,15 +322,15 @@ class Ingo_Script_Sieve extends Ingo_Script {
         }
 
         if ($temp) {
-            $test = new Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'addresses' => implode("\n", $temp)));
-            $if = new Sieve_If($test);
+            $test = new Ingo_Script_Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'addresses' => implode("\n", $temp)));
+            $if = new Ingo_Script_Sieve_If($test);
             $if->setActions($action);
             $this->_blocks[] = $if;
         }
 
         if ($wildcards) {
-            $test = new Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'match-type' => ':matches', 'addresses' => implode("\n", $wildcards)));
-            $if = new Sieve_If($test);
+            $test = new Ingo_Script_Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'match-type' => ':matches', 'addresses' => implode("\n", $wildcards)));
+            $if = new Ingo_Script_Sieve_If($test);
             $if->setActions($action);
             $this->_blocks[] = $if;
         }
@@ -327,7 +339,7 @@ class Ingo_Script_Sieve extends Ingo_Script {
     /**
      * Adds all blocks necessary for the whitelist rule.
      */
-    function _addWhitelistBlocks()
+    protected function _addWhitelistBlocks()
     {
         if (!$this->_validRule(Ingo_Storage::ACTION_WHITELIST)) {
             return;
@@ -339,11 +351,11 @@ class Ingo_Script_Sieve extends Ingo_Script {
             return;
         }
 
-        $this->_blocks[] = new Sieve_Comment($this->encode(_("Whitelisted Addresses")));
+        $this->_blocks[] = new Ingo_Script_Sieve_Comment($this->encode(_("Whitelisted Addresses")));
 
-        $action = array(new Sieve_Action_Keep(), new Sieve_Action_Stop());
-        $test = new Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'addresses' => implode("\n", $wl_addr)));
-        $if = new Sieve_If($test);
+        $action = array(new Ingo_Script_Sieve_Action_Keep(), new Ingo_Script_Sieve_Action_Stop());
+        $test = new Ingo_Script_Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'addresses' => implode("\n", $wl_addr)));
+        $if = new Ingo_Script_Sieve_If($test);
         $if->setActions($action);
         $this->_blocks[] = $if;
     }
@@ -351,7 +363,7 @@ class Ingo_Script_Sieve extends Ingo_Script {
     /**
      * Adds all blocks necessary for the vacation rule.
      */
-    function _addVacationBlocks()
+    protected function _addVacationBlocks()
     {
         if (!$this->_validRule(Ingo_Storage::ACTION_VACATION)) {
             return;
@@ -379,26 +391,26 @@ class Ingo_Script_Sieve extends Ingo_Script {
         );
 
         $action = $tests = array();
-        $action[] = new Sieve_Action_Vacation($vals);
+        $action[] = new Ingo_Script_Sieve_Action_Vacation($vals);
 
         if ($vacation->getVacationIgnorelist()) {
             $mime_headers = new Horde_Mime_Headers();
             $headers = $mime_headers->listHeaders();
             $headers['Mailing-List'] = null;
-            $tmp = new Sieve_Test_Exists(array('headers' => implode("\n", array_keys($headers))));
-            $tests[] = new Sieve_Test_Not($tmp);
+            $tmp = new Ingo_Script_Sieve_Test_Exists(array('headers' => implode("\n", array_keys($headers))));
+            $tests[] = new Ingo_Script_Sieve_Test_Not($tmp);
             $vals = array('headers' => 'Precedence',
                           'match-type' => ':is',
                           'strings' => "list\nbulk\njunk",
                           'comparator' => 'i;ascii-casemap');
-            $tmp = new Sieve_Test_Header($vals);
-            $tests[] = new Sieve_Test_Not($tmp);
+            $tmp = new Ingo_Script_Sieve_Test_Header($vals);
+            $tests[] = new Ingo_Script_Sieve_Test_Not($tmp);
             $vals = array('headers' => 'To',
                           'match-type' => ':matches',
                           'strings' => 'Multiple recipients of*',
                           'comparator' => 'i;ascii-casemap');
-            $tmp = new Sieve_Test_Header($vals);
-            $tests[] = new Sieve_Test_Not($tmp);
+            $tmp = new Ingo_Script_Sieve_Test_Header($vals);
+            $tests[] = new Ingo_Script_Sieve_Test_Not($tmp);
         }
 
         $addrs = array();
@@ -410,15 +422,15 @@ class Ingo_Script_Sieve extends Ingo_Script {
         }
 
         if ($addrs) {
-            $tmp = new Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'addresses' => implode("\n", $addrs)));
-            $tests[] = new Sieve_Test_Not($tmp);
+            $tmp = new Ingo_Script_Sieve_Test_Address(array('headers' => "From\nSender\nResent-From", 'addresses' => implode("\n", $addrs)));
+            $tests[] = new Ingo_Script_Sieve_Test_Not($tmp);
         }
 
-        $this->_blocks[] = new Sieve_Comment($this->encode(_("Vacation")));
+        $this->_blocks[] = new Ingo_Script_Sieve_Comment($this->encode(_("Vacation")));
 
         if ($tests) {
-            $test = new Sieve_Test_Allof($tests);
-            $if = new Sieve_If($test);
+            $test = new Ingo_Script_Sieve_Test_Allof($tests);
+            $if = new Ingo_Script_Sieve_If($test);
             $if->setActions($action);
             $this->_blocks[] = $if;
         } else {
@@ -429,21 +441,21 @@ class Ingo_Script_Sieve extends Ingo_Script {
     /**
      * Adds all blocks necessary for the spam rule.
      */
-    function _addSpamBlocks()
+    protected function _addSpamBlocks()
     {
         if (!$this->_validRule(Ingo_Storage::ACTION_SPAM)) {
             return;
         }
 
-        $spam = &$GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_SPAM);
+        $spam = $GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_SPAM);
         if ($spam === false) {
             return;
         }
 
-        $this->_blocks[] = new Sieve_Comment($this->encode(_("Spam Filter")));
+        $this->_blocks[] = new Ingo_Script_Sieve_Comment($this->encode(_("Spam Filter")));
 
         $actions = array();
-        $actions[] = new Sieve_Action_Fileinto(array_merge($this->_params, array('folder' => $spam->getSpamFolder())));
+        $actions[] = new Ingo_Script_Sieve_Action_Fileinto(array_merge($this->_params, array('folder' => $spam->getSpamFolder())));
 
         if ($this->_params['spam_compare'] == 'numeric') {
             $vals = array(
@@ -451,7 +463,7 @@ class Ingo_Script_Sieve extends Ingo_Script {
                 'comparison' => 'ge',
                 'value' => $spam->getSpamLevel(),
             );
-            $test = new Sieve_Test_Relational($vals);
+            $test = new Ingo_Script_Sieve_Test_Relational($vals);
         } elseif ($this->_params['spam_compare'] == 'string') {
             $vals = array(
                 'headers' => $this->_params['spam_header'],
@@ -460,12 +472,12 @@ class Ingo_Script_Sieve extends Ingo_Script {
                                         $spam->getSpamLevel()),
                 'comparator' => 'i;ascii-casemap',
             );
-            $test = new Sieve_Test_Header($vals);
+            $test = new Ingo_Script_Sieve_Test_Header($vals);
         }
 
-        $actions[] = new Sieve_Action_Stop();
+        $actions[] = new Ingo_Script_Sieve_Action_Stop();
 
-        $if = new Sieve_If($test);
+        $if = new Ingo_Script_Sieve_If($test);
         $if->setActions($actions);
         $this->_blocks[] = $if;
     }
@@ -476,7 +488,7 @@ class Ingo_Script_Sieve extends Ingo_Script {
      *
      * @return string  The Sieve script.
      */
-    function generate()
+    public function generate()
     {
         global $ingo_storage;
 
@@ -493,74 +505,74 @@ class Ingo_Script_Sieve extends Ingo_Script {
             switch ($filter['action']) {
             case Ingo_Storage::ACTION_KEEP:
                 if (!empty($filter['flags'])) {
-                    $action[] = new Sieve_Action_Addflag(array('flags' => $filter['flags']));
+                    $action[] = new Ingo_Script_Sieve_Action_Addflag(array('flags' => $filter['flags']));
                 }
 
-                $action[] = new Sieve_Action_Keep();
+                $action[] = new Ingo_Script_Sieve_Action_Keep();
 
                 if (!empty($filter['flags'])) {
-                    $action[] = new Sieve_Action_RemoveFlag(array('flags' => $filter['flags']));
+                    $action[] = new Ingo_Script_Sieve_Action_RemoveFlag(array('flags' => $filter['flags']));
                 }
                 break;
 
             case Ingo_Storage::ACTION_DISCARD:
-                $action[] = new Sieve_Action_Discard();
+                $action[] = new Ingo_Script_Sieve_Action_Discard();
                 break;
 
             case Ingo_Storage::ACTION_MOVE:
                 if (!empty($filter['flags'])) {
-                    $action[] = new Sieve_Action_Addflag(array('flags' => $filter['flags']));
+                    $action[] = new Ingo_Script_Sieve_Action_Addflag(array('flags' => $filter['flags']));
                 }
 
-                $action[] = new Sieve_Action_Fileinto(array_merge($this->_params, array('folder' => $filter['action-value'])));
+                $action[] = new Ingo_Script_Sieve_Action_Fileinto(array_merge($this->_params, array('folder' => $filter['action-value'])));
 
                 if (!empty($filter['flags'])) {
-                    $action[] = new Sieve_Action_RemoveFlag(array('flags' => $filter['flags']));
+                    $action[] = new Ingo_Script_Sieve_Action_RemoveFlag(array('flags' => $filter['flags']));
                     }
                 break;
 
             case Ingo_Storage::ACTION_REJECT:
-                $action[] = new Sieve_Action_Reject(array('reason' => $filter['action-value']));
+                $action[] = new Ingo_Script_Sieve_Action_Reject(array('reason' => $filter['action-value']));
                 break;
 
             case Ingo_Storage::ACTION_REDIRECT:
-                $action[] = new Sieve_Action_Redirect(array('address' => $filter['action-value']));
+                $action[] = new Ingo_Script_Sieve_Action_Redirect(array('address' => $filter['action-value']));
                 break;
 
             case Ingo_Storage::ACTION_REDIRECTKEEP:
                 if (!empty($filter['flags'])) {
-                    $action[] = new Sieve_Action_Addflag(array('flags' => $filter['flags']));
+                    $action[] = new Ingo_Script_Sieve_Action_Addflag(array('flags' => $filter['flags']));
                 }
 
-                $action[] = new Sieve_Action_Redirect(array('address' => $filter['action-value']));
-                $action[] = new Sieve_Action_Keep();
+                $action[] = new Ingo_Script_Sieve_Action_Redirect(array('address' => $filter['action-value']));
+                $action[] = new Ingo_Script_Sieve_Action_Keep();
 
                 if (!empty($filter['flags'])) {
-                    $action[] = new Sieve_Action_RemoveFlag(array('flags' => $filter['flags']));
+                    $action[] = new Ingo_Script_Sieve_Action_RemoveFlag(array('flags' => $filter['flags']));
                 }
                 break;
 
             case Ingo_Storage::ACTION_MOVEKEEP:
                 if (!empty($filter['flags'])) {
-                    $action[] = new Sieve_Action_Addflag(array('flags' => $filter['flags']));
+                    $action[] = new Ingo_Script_Sieve_Action_Addflag(array('flags' => $filter['flags']));
                 }
 
-                $action[] = new Sieve_Action_Keep();
-                $action[] = new Sieve_Action_Fileinto(array_merge($this->_params, array('folder' => $filter['action-value'])));
+                $action[] = new Ingo_Script_Sieve_Action_Keep();
+                $action[] = new Ingo_Script_Sieve_Action_Fileinto(array_merge($this->_params, array('folder' => $filter['action-value'])));
 
                 if (!empty($filter['flags'])) {
-                    $action[] = new Sieve_Action_RemoveFlag(array('flags' => $filter['flags']));
+                    $action[] = new Ingo_Script_Sieve_Action_RemoveFlag(array('flags' => $filter['flags']));
                 }
                 break;
 
             case Ingo_Storage::ACTION_FLAGONLY:
                 if (!empty($filter['flags'])) {
-                    $action[] = new Sieve_Action_Addflag(array('flags' => $filter['flags']));
+                    $action[] = new Ingo_Script_Sieve_Action_Addflag(array('flags' => $filter['flags']));
                 }
                 break;
 
             case Ingo_Storage::ACTION_NOTIFY:
-                $action[] = new Sieve_Action_Notify(array('address' => $filter['action-value'], 'name' => $filter['name']));
+                $action[] = new Ingo_Script_Sieve_Action_Notify(array('address' => $filter['action-value'], 'name' => $filter['name']));
                 break;
 
             case Ingo_Storage::ACTION_WHITELIST:
@@ -584,72 +596,72 @@ class Ingo_Script_Sieve extends Ingo_Script {
                 continue 2;
             }
 
-            $this->_blocks[] = new Sieve_Comment($this->encode($filter['name']));
+            $this->_blocks[] = new Ingo_Script_Sieve_Comment($this->encode($filter['name']));
 
             if ($filter['stop']) {
-                $action[] = new Sieve_Action_Stop();
+                $action[] = new Ingo_Script_Sieve_Action_Stop();
             }
 
-            $test = new Sieve_Test();
+            $test = new Ingo_Script_Sieve_Test();
             if ($filter['combine'] == Ingo_Storage::COMBINE_ANY) {
-                $test = new Sieve_Test_Anyof();
+                $test = new Ingo_Script_Sieve_Test_Anyof();
             } else {
-                $test = new Sieve_Test_Allof();
+                $test = new Ingo_Script_Sieve_Test_Allof();
             }
 
             foreach ($filter['conditions'] as $condition) {
                 $tmp = '';
                 switch ($condition['match']) {
                 case 'equal':
-                    $tmp = new Sieve_Test_Relational(array('comparison' => 'eq', 'headers' => $condition['field'], 'value' => $condition['value']));
+                    $tmp = new Ingo_Script_Sieve_Test_Relational(array('comparison' => 'eq', 'headers' => $condition['field'], 'value' => $condition['value']));
                     $test->addTest($tmp);
                     break;
 
                 case 'not equal':
-                    $tmp = new Sieve_Test_Relational(array('comparison' => 'ne', 'headers' => $condition['field'], 'value' => $condition['value']));
+                    $tmp = new Ingo_Script_Sieve_Test_Relational(array('comparison' => 'ne', 'headers' => $condition['field'], 'value' => $condition['value']));
                     $test->addTest($tmp);
                     break;
 
                 case 'less than':
                     if ($condition['field'] == 'Size') {
                         /* Message Size Test. */
-                        $tmp = new Sieve_Test_Size(array('comparison' => ':under', 'size' => $condition['value']));
+                        $tmp = new Ingo_Script_Sieve_Test_Size(array('comparison' => ':under', 'size' => $condition['value']));
                     } else {
                         /* Relational Test. */
-                        $tmp = new Sieve_Test_Relational(array('comparison' => 'lt', 'headers' => $condition['field'], 'value' => $condition['value']));
+                        $tmp = new Ingo_Script_Sieve_Test_Relational(array('comparison' => 'lt', 'headers' => $condition['field'], 'value' => $condition['value']));
                     }
                     $test->addTest($tmp);
                     break;
 
                 case 'less than or equal to':
-                    $tmp = new Sieve_Test_Relational(array('comparison' => 'le', 'headers' => $condition['field'], 'value' => $condition['value']));
+                    $tmp = new Ingo_Script_Sieve_Test_Relational(array('comparison' => 'le', 'headers' => $condition['field'], 'value' => $condition['value']));
                     $test->addTest($tmp);
                     break;
 
                 case 'greater than':
                     if ($condition['field'] == 'Size') {
                         /* Message Size Test. */
-                        $tmp = new Sieve_Test_Size(array('comparison' => ':over', 'size' => $condition['value']));
+                        $tmp = new Ingo_Script_Sieve_Test_Size(array('comparison' => ':over', 'size' => $condition['value']));
                     } else {
                         /* Relational Test. */
-                        $tmp = new Sieve_Test_Relational(array('comparison' => 'gt', 'headers' => $condition['field'], 'value' => $condition['value']));
+                        $tmp = new Ingo_Script_Sieve_Test_Relational(array('comparison' => 'gt', 'headers' => $condition['field'], 'value' => $condition['value']));
                     }
                     $test->addTest($tmp);
                     break;
 
                 case 'greater than or equal to':
-                    $tmp = new Sieve_Test_Relational(array('comparison' => 'ge', 'headers' => $condition['field'], 'value' => $condition['value']));
+                    $tmp = new Ingo_Script_Sieve_Test_Relational(array('comparison' => 'ge', 'headers' => $condition['field'], 'value' => $condition['value']));
                     $test->addTest($tmp);
                     break;
 
                 case 'exists':
-                    $tmp = new Sieve_Test_Exists(array('headers' => $condition['field']));
+                    $tmp = new Ingo_Script_Sieve_Test_Exists(array('headers' => $condition['field']));
                     $test->addTest($tmp);
                     break;
 
                 case 'not exist':
-                    $tmp = new Sieve_Test_Exists(array('headers' => $condition['field']));
-                    $test->addTest(new Sieve_Test_Not($tmp));
+                    $tmp = new Ingo_Script_Sieve_Test_Exists(array('headers' => $condition['field']));
+                    $test->addTest(new Ingo_Script_Sieve_Test_Not($tmp));
                     break;
 
                 case 'contains':
@@ -692,11 +704,11 @@ class Ingo_Script_Sieve extends Ingo_Script {
                     case 'contains':
                         $vals['match-type'] = ':contains';
                         if ($use_address_test) {
-                            $tmp = new Sieve_Test_Address($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Address($vals);
                         } elseif ($condition['field'] == 'Body') {
-                            $tmp = new Sieve_Test_Body($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Body($vals);
                         } else {
-                            $tmp = new Sieve_Test_Header($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Header($vals);
                         }
                         $test->addTest($tmp);
                         break;
@@ -704,23 +716,23 @@ class Ingo_Script_Sieve extends Ingo_Script {
                     case 'not contain':
                         $vals['match-type'] = ':contains';
                         if ($use_address_test) {
-                            $tmp = new Sieve_Test_Address($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Address($vals);
                         } elseif ($condition['field'] == 'Body') {
-                            $tmp = new Sieve_Test_Body($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Body($vals);
                         } else {
-                            $tmp = new Sieve_Test_Header($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Header($vals);
                         }
-                        $test->addTest(new Sieve_Test_Not($tmp));
+                        $test->addTest(new Ingo_Script_Sieve_Test_Not($tmp));
                         break;
 
                     case 'is':
                         $vals['match-type'] = ':is';
                         if ($use_address_test) {
-                            $tmp = new Sieve_Test_Address($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Address($vals);
                         } elseif ($condition['field'] == 'Body') {
-                            $tmp = new Sieve_Test_Body($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Body($vals);
                         } else {
-                            $tmp = new Sieve_Test_Header($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Header($vals);
                         }
                         $test->addTest($tmp);
                         break;
@@ -728,13 +740,13 @@ class Ingo_Script_Sieve extends Ingo_Script {
                     case 'not is':
                         $vals['match-type'] = ':is';
                         if ($use_address_test) {
-                            $tmp = new Sieve_Test_Address($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Address($vals);
                         } elseif ($condition['field'] == 'Body') {
-                            $tmp = new Sieve_Test_Body($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Body($vals);
                         } else {
-                            $tmp = new Sieve_Test_Header($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Header($vals);
                         }
-                        $test->addTest(new Sieve_Test_Not($tmp));
+                        $test->addTest(new Ingo_Script_Sieve_Test_Not($tmp));
                         break;
 
                     case 'begins with':
@@ -749,7 +761,7 @@ class Ingo_Script_Sieve extends Ingo_Script {
                             } else {
                                 $vals['addresses'] .= '*';
                             }
-                            $tmp = new Sieve_Test_Address($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Address($vals);
                         } else {
                             $add_arr = preg_split('(\r\n|\n|\r)', $vals['strings']);
                             if (count($add_arr) > 1) {
@@ -761,9 +773,9 @@ class Ingo_Script_Sieve extends Ingo_Script {
                                 $vals['strings'] .= '*';
                             }
                             if ($condition['field'] == 'Body') {
-                                $tmp = new Sieve_Test_Body($vals);
+                                $tmp = new Ingo_Script_Sieve_Test_Body($vals);
                             } else {
-                                $tmp = new Sieve_Test_Header($vals);
+                                $tmp = new Ingo_Script_Sieve_Test_Header($vals);
                             }
                         }
                         $test->addTest($tmp);
@@ -781,7 +793,7 @@ class Ingo_Script_Sieve extends Ingo_Script {
                             } else {
                                 $vals['addresses'] .= '*';
                             }
-                            $tmp = new Sieve_Test_Address($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Address($vals);
                         } else {
                             $add_arr = preg_split('(\r\n|\n|\r)', $vals['strings']);
                             if (count($add_arr) > 1) {
@@ -793,12 +805,12 @@ class Ingo_Script_Sieve extends Ingo_Script {
                                 $vals['strings'] .= '*';
                             }
                             if ($condition['field'] == 'Body') {
-                                $tmp = new Sieve_Test_Body($vals);
+                                $tmp = new Ingo_Script_Sieve_Test_Body($vals);
                             } else {
-                                $tmp = new Sieve_Test_Header($vals);
+                                $tmp = new Ingo_Script_Sieve_Test_Header($vals);
                             }
                         }
-                        $test->addTest(new Sieve_Test_Not($tmp));
+                        $test->addTest(new Ingo_Script_Sieve_Test_Not($tmp));
                         break;
 
                     case 'ends with':
@@ -813,7 +825,7 @@ class Ingo_Script_Sieve extends Ingo_Script {
                             } else {
                                 $vals['addresses'] = '*' .  $vals['addresses'];
                             }
-                            $tmp = new Sieve_Test_Address($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Address($vals);
                         } else {
                             $add_arr = preg_split('(\r\n|\n|\r)', $vals['strings']);
                             if (count($add_arr) > 1) {
@@ -825,9 +837,9 @@ class Ingo_Script_Sieve extends Ingo_Script {
                                 $vals['strings'] = '*' .  $vals['strings'];
                             }
                             if ($condition['field'] == 'Body') {
-                                $tmp = new Sieve_Test_Body($vals);
+                                $tmp = new Ingo_Script_Sieve_Test_Body($vals);
                             } else {
-                                $tmp = new Sieve_Test_Header($vals);
+                                $tmp = new Ingo_Script_Sieve_Test_Header($vals);
                             }
                         }
                         $test->addTest($tmp);
@@ -845,7 +857,7 @@ class Ingo_Script_Sieve extends Ingo_Script {
                             } else {
                                 $vals['addresses'] = '*' .  $vals['addresses'];
                             }
-                            $tmp = new Sieve_Test_Address($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Address($vals);
                         } else {
                             $add_arr = preg_split('(\r\n|\n|\r)', $vals['strings']);
                             if (count($add_arr) > 1) {
@@ -857,22 +869,22 @@ class Ingo_Script_Sieve extends Ingo_Script {
                                 $vals['strings'] = '*' .  $vals['strings'];
                             }
                             if ($condition['field'] == 'Body') {
-                                $tmp = new Sieve_Test_Body($vals);
+                                $tmp = new Ingo_Script_Sieve_Test_Body($vals);
                             } else {
-                                $tmp = new Sieve_Test_Header($vals);
+                                $tmp = new Ingo_Script_Sieve_Test_Header($vals);
                             }
                         }
-                        $test->addTest(new Sieve_Test_Not($tmp));
+                        $test->addTest(new Ingo_Script_Sieve_Test_Not($tmp));
                         break;
 
                     case 'regex':
                         $vals['match-type'] = ':regex';
                         if ($use_address_test) {
-                            $tmp = new Sieve_Test_Address($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Address($vals);
                         } elseif ($condition['field'] == 'Body') {
-                            $tmp = new Sieve_Test_Body($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Body($vals);
                         } else {
-                            $tmp = new Sieve_Test_Header($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Header($vals);
                         }
                         $test->addTest($tmp);
                         break;
@@ -880,11 +892,11 @@ class Ingo_Script_Sieve extends Ingo_Script {
                     case 'matches':
                         $vals['match-type'] = ':matches';
                         if ($use_address_test) {
-                            $tmp = new Sieve_Test_Address($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Address($vals);
                         } elseif ($condition['field'] == 'Body') {
-                            $tmp = new Sieve_Test_Body($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Body($vals);
                         } else {
-                            $tmp = new Sieve_Test_Header($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Header($vals);
                         }
                         $test->addTest($tmp);
                         break;
@@ -892,19 +904,19 @@ class Ingo_Script_Sieve extends Ingo_Script {
                     case 'not matches':
                         $vals['match-type'] = ':matches';
                         if ($use_address_test) {
-                            $tmp = new Sieve_Test_Address($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Address($vals);
                         } elseif ($condition['field'] == 'Body') {
-                            $tmp = new Sieve_Test_Body($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Body($vals);
                         } else {
-                            $tmp = new Sieve_Test_Header($vals);
+                            $tmp = new Ingo_Script_Sieve_Test_Header($vals);
                         }
-                        $test->addTest(new Sieve_Test_Not($tmp));
+                        $test->addTest(new Ingo_Script_Sieve_Test_Not($tmp));
                         break;
                     }
                 }
             }
 
-            $if = new Sieve_If($test);
+            $if = new Ingo_Script_Sieve_If($test);
             $if->setActions($action);
             $this->_blocks[] = $if;
         }
@@ -918,2062 +930,3 @@ class Ingo_Script_Sieve extends Ingo_Script {
     }
 
 }
-
-/**
- * The Sieve_If class represents a Sieve If Statement
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Sieve_If {
-
-    /**
-     * The Sieve_Test object for the if test.
-     *
-     * @var Sieve_Test
-     */
-    var $_test;
-
-    /**
-     * A list of Sieve_Action objects that go into the if clause.
-     *
-     * @var array
-     */
-    var $_actions = array();
-
-    /**
-     * A list of Sieve_Elseif objects that create optional elsif clauses.
-     *
-     * @var array
-     */
-    var $_elsifs = array();
-
-    /**
-     * A Sieve_Else object that creates an optional else clause.
-     *
-     * @var Sieve_Else
-     */
-    var $_else;
-
-    /**
-     * Constructor.
-     *
-     * @param Sieve_Test $test  A Sieve_Test object.
-     */
-    function Sieve_If($test = null)
-    {
-        if (is_null($test)) {
-            $this->_test = new Sieve_Test_False();
-        } else {
-            $this->_test = $test;
-        }
-
-        $this->_actions[] = new Sieve_Action_Keep();
-        $this->_else = new Sieve_Else();
-    }
-
-    function getTest()
-    {
-        return $this->_test;
-    }
-
-    function setTest($test)
-    {
-        $this->_test = $test;
-    }
-
-    function getActions()
-    {
-        return $this->_actions;
-    }
-
-    function setActions($actions)
-    {
-        $this->_actions = $actions;
-    }
-
-    function getElsifs()
-    {
-        return $this->_elsifs;
-    }
-
-    function setElsifs($elsifs)
-    {
-        $this->_elsifs = $elsifs;
-    }
-
-    function addElsif($elsif)
-    {
-        $this->_elsifs[] = $elsif;
-    }
-
-    function getElse()
-    {
-        return $this->_else;
-    }
-
-    function setElse($else)
-    {
-        $this->_else = $else;
-    }
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        $code = 'if ' . $this->_test->toCode() . " { \n";
-        foreach ($this->_actions as $action) {
-            $code .= '    ' . $action->toCode() . "\n";
-        }
-        $code .= "} ";
-
-        foreach ($this->_elsifs as $elsif) {
-            $code .= $elsif->toCode();
-        }
-
-        $code .= $this->_else->toCode();
-
-        return $code . "\n";
-    }
-
-    /**
-     * Checks if all sub-rules are valid.
-     *
-     * @return boolean|string  True if all rules are valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        $res = $this->_test->check();
-        if ($res !== true) {
-            return $res;
-        }
-
-        foreach ($this->_elsifs as $elsif) {
-            $res = $elsif->check();
-            if ($res !== true) {
-                return $res;
-            }
-        }
-
-        $res = $this->_else->check();
-        if ($res !== true) {
-            return $res;
-        }
-
-        foreach ($this->_actions as $action) {
-            $res = $action->check();
-            if ($res !== true) {
-                return $res;
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Returns a list of sieve extensions required for this rule and any
-     * sub-rules.
-     *
-     * @return array  A Sieve extension list.
-     */
-    function requires()
-    {
-        $requires = array();
-
-        foreach ($this->_actions as $action) {
-            $requires = array_merge($requires, $action->requires());
-        }
-
-        foreach ($this->_elsifs as $elsif) {
-            $requires = array_merge($requires, $elsif->requires());
-        }
-
-        $requires = array_merge($requires, $this->_test->requires(), $this->_else->requires());
-
-        return $requires;
-    }
-
-}
-
-/**
- * The Sieve_Else class represents a Sieve Else Statement
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Sieve_Else {
-
-    /**
-     * A list of Sieve_Action objects that go into the else clause.
-     *
-     * @var array
-     */
-    var $_actions = array();
-
-    /**
-     * Constructor.
-     *
-     * @param Sieve_Action|array $actions  A Sieve_Action object or a list of
-     *                                      Sieve_Action objects.
-     */
-    function Sieve_Else($actions = null)
-    {
-        if (is_array($actions)) {
-            $this->_actions = $actions;
-        } elseif (!is_null($actions)) {
-            $this->_actions[] = $actions;
-        }
-    }
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-     function toCode()
-     {
-        if (count($this->_actions) == 0) {
-            return '';
-        }
-
-        $code = 'else' . " { \n";
-        foreach ($this->_actions as $action) {
-            $code .= '    ' . $action->toCode() . "\n";
-        }
-        $code .= "} ";
-
-        return $code;
-    }
-
-    function setActions($actions)
-    {
-        $this->_actions = $actions;
-    }
-
-    function getActions()
-    {
-        return $this->_actions;
-    }
-
-    /**
-     * Checks if all sub-rules are valid.
-     *
-     * @return boolean|string  True if all rules are valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        foreach ($this->_actions as $action) {
-            $res = $action->check();
-            if ($res !== true) {
-                return $res;
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Returns a list of sieve extensions required for this rule and any
-     * sub-rules.
-     *
-     * @return array  A Sieve extension list.
-     */
-    function requires()
-    {
-        $requires = array();
-
-        foreach ($this->_actions as $action) {
-            $requires = array_merge($requires, $action->requires());
-        }
-
-        return $requires;
-    }
-
-}
-
-/**
- * The Sieve_Elsif class represents a Sieve Elsif Statement
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Sieve_Elsif {
-
-    /**
-     * The Sieve_Test object for the if test.
-     *
-     * @var Sieve_Test
-     */
-    var $_test;
-
-    /**
-     * A list of Sieve_Action objects that go into the if clause.
-     *
-     * @var array
-     */
-    var $_actions = array();
-
-    /**
-     * Constructor.
-     *
-     * @param Sieve_Test $test  A Sieve_Test object.
-     */
-    function Sieve_Elsif($test = null)
-    {
-        if (is_null($test)) {
-            $this->_test = new Sieve_Test_False();
-        } else {
-            $this->_test = $test;
-        }
-        $this->_actions[] = new Sieve_Action_Keep();
-    }
-
-    function getTest()
-    {
-        return $this->_test;
-    }
-
-    function setTest($test)
-    {
-        $this->_test = $test;
-    }
-
-    function getActions()
-    {
-        return $this->_actions;
-    }
-
-    function setActions($actions)
-    {
-        $this->_actions = $actions;
-    }
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        $code = 'elsif ' . $this->_test->toCode() . " { \n";
-        foreach ($this->_actions as $action) {
-            $code .= '    ' . $action->toCode() . "\n";
-        }
-        $code .= "} ";
-
-        return $code;
-    }
-
-    /**
-     * Checks if all sub-rules are valid.
-     *
-     * @return boolean|string  True if all rules are valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        $res = $this->_test->check();
-        if ($res !== true) {
-            return $res;
-        }
-
-        foreach ($this->_actions as $action) {
-            $res = $action->check();
-            if ($res !== true) {
-                return $res;
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Returns a list of sieve extensions required for this rule and any
-     * sub-rules.
-     *
-     * @return array  A Sieve extension list.
-     */
-    function requires()
-    {
-        $requires = array();
-
-        foreach ($this->_actions as $action) {
-            $requires = array_merge($requires, $action->requires());
-        }
-
-        $requires = array_merge($requires, $this->_test->requires());
-
-        return $requires;
-    }
-
-}
-
-/**
- * The Sieve_Test class represents a Sieve Test.
- *
- * A test is a piece of code that evaluates to true or false.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Sieve_Test {
-
-    /**
-     * Any necessary test parameters.
-     *
-     * @var array
-     */
-    var $_vars = array();
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        return 'toCode() Function Not Implemented in class ' . get_class($this);
-    }
-
-    /**
-     * Checks if the rule parameters are valid.
-     *
-     * @return boolean|string  True if this rule is valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        return 'check() Function Not Implemented in class ' . get_class($this);
-    }
-
-    /**
-     * Returns a list of sieve extensions required for this rule and any
-     * sub-rules.
-     *
-     * @return array  A Sieve extension list.
-     */
-    function requires()
-    {
-        return array();
-    }
-
-}
-
-/**
- * The Sieve_Test_True class represents a test that always evaluates to true.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Sieve_Test_True extends Sieve_Test {
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        return 'true';
-    }
-
-    /**
-     * Checks if the rule parameters are valid.
-     *
-     * @return boolean|string  True if this rule is valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        return true;
-    }
-
-}
-
-/**
- * The Sieve_Test_False class represents a test that always evaluates to
- * false.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Sieve_Test_False extends Sieve_Test {
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        return 'false';
-    }
-
-    /**
-     * Checks if the rule parameters are valid.
-     *
-     * @return boolean|string  True if this rule is valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        return true;
-    }
-
-}
-
-/**
- * The Sieve_Test_Allof class represents a Allof test structure.
- *
- * Equivalent to a logical AND of all the tests it contains.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Sieve_Test_Allof extends Sieve_Test {
-
-    var $_tests = array();
-
-    /**
-     * Constructor.
-     *
-     * @param Sieve_Test|array $test  A Sieve_Test object or a list of
-     *                                 Sieve_Test objects.
-     */
-    function Sieve_Test_Allof($test = null)
-    {
-        if (is_array($test)) {
-            $this->_tests = $test;
-        } elseif (!is_null($test)) {
-            $this->_tests[] = $test;
-        }
-    }
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        $code = '';
-        if (count($this->_tests) > 1) {
-            $testlist = '';
-            foreach ($this->_tests as $test) {
-                $testlist .= (empty($testlist)) ? '' : ', ';
-                $testlist .= trim($test->toCode());
-            }
-
-            $code = "allof ( $testlist )";
-        } elseif (count($this->_tests) == 1) {
-            $code = $this->_tests[0]->toCode();
-        } else {
-            return 'true';
-        }
-        return $code;
-    }
-
-    /**
-     * Checks if all sub-rules are valid.
-     *
-     * @return boolean|string  True if all rules are valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        foreach ($this->_tests as $test) {
-            $res = $test->check();
-            if ($res !== true) {
-                return $res;
-            }
-        }
-
-        return true;
-    }
-
-    function addTest($test)
-    {
-        $this->_tests[] = $test;
-    }
-
-    function getTests()
-    {
-        return $this->_tests;
-    }
-
-    /**
-     * Returns a list of sieve extensions required for this rule and any
-     * sub-rules.
-     *
-     * @return array  A Sieve extension list.
-     */
-    function requires()
-    {
-        $requires = array();
-
-        foreach ($this->_tests as $test) {
-            $requires = array_merge($requires, $test->requires());
-        }
-
-        return $requires;
-    }
-
-}
-
-/**
- * The Sieve_Test_Anyof class represents a Anyof test structure.
- *
- * Equivalent to a logical OR of all the tests it contains.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Sieve_Test_Anyof extends Sieve_Test {
-
-    var $_tests = array();
-
-    /**
-     * Constructor.
-     *
-     * @param Sieve_Test|array $test  A Sieve_Test object or a list of
-     *                                 Sieve_Test objects.
-     */
-    function Sieve_Test_Anyof($test = null)
-    {
-        if (is_array($test)) {
-            $this->_tests = $test;
-        } elseif (!is_null($test)) {
-            $this->_tests[] = $test;
-        }
-    }
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        $testlist = '';
-        if (count($this->_tests) > 1) {
-            $testlist = '';
-            foreach ($this->_tests as $test) {
-                $testlist .= (empty($testlist)) ? '' : ', ';
-                $testlist .= trim($test->toCode());
-            }
-
-            $code = "anyof ( $testlist )";
-        } elseif (count($this->_tests) == 1) {
-            $code = $this->_tests[0]->toCode();
-        } else {
-            return 'true';
-        }
-        return $code;
-    }
-
-    function addTest($test)
-    {
-        $this->_tests[] = $test;
-    }
-
-    function getTests()
-    {
-        return $this->_tests;
-    }
-
-    /**
-     * Checks if all sub-rules are valid.
-     *
-     * @return boolean|string  True if all rules are valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        foreach ($this->_tests as $test) {
-            $res = $test->check();
-            if ($res !== true) {
-                return $res;
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Returns a list of sieve extensions required for this rule and any
-     * sub-rules.
-     *
-     * @return array  A Sieve extension list.
-     */
-    function requires()
-    {
-        $requires = array();
-
-        foreach ($this->_tests as $test) {
-            $requires = array_merge($requires, $test->requires());
-        }
-
-        return $requires;
-    }
-
-}
-
-/**
- * The Sieve_Test_Relational class represents a relational test.
- *
- * @author  Todd Merritt <tmerritt@email.arizona.edu>
- * @package Ingo
- */
-class Sieve_Test_Relational extends Sieve_Test {
-
-    /**
-     * Constructor.
-     *
-     * @param array $vars  Any required parameters.
-     */
-    function Sieve_Test_Relational($vars = array())
-    {
-        $this->_vars['comparison'] = (isset($vars['comparison'])) ? $vars['comparison'] : '';
-        $this->_vars['headers'] = (isset($vars['headers'])) ? $vars['headers'] : '';
-        $this->_vars['value'] = (isset($vars['value'])) ? $vars['value'] : 0;
-    }
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        $code = 'header :value "' .
-            $this->_vars['comparison'] . '" ' .
-            ':comparator "i;ascii-numeric" ';
-
-        $headers = preg_split('(\r\n|\n|\r)', $this->_vars['headers']);
-        $header_count = count($headers);
-
-        if ($header_count > 1) {
-            $code .= "[";
-            $headerstr = '';
-
-            foreach ($headers as $val) {
-                $headerstr .= (empty($headerstr) ? '"' : ', "') .
-                    Ingo_Script_Sieve::escapeString($val) . '"';
-            }
-
-            $code .= $headerstr . '] ';
-            $headerstr = '[' . $headerstr . ']';
-        } elseif ($header_count == 1) {
-            $code .= '"' . Ingo_Script_Sieve::escapeString($headers[0]) . '" ';
-            $headerstr = Ingo_Script_Sieve::escapeString($headers[0]);
-        }
-
-        $code .= '["' . $this->_vars['value'] . '"]';
-
-        // Add workarounds for negative numbers - works only if the comparison
-        // value is positive. Sieve doesn't support comparisons of negative
-        // numbers at all so this is the best we can do.
-        switch ($this->_vars['comparison']) {
-        case 'gt':
-        case 'ge':
-        case 'eq':
-            // Greater than, greater or equal, equal: number must be
-            // non-negative.
-            return 'allof ( not header :comparator "i;ascii-casemap" :contains "'
-                . $headerstr . '" "-", ' . $code . ' )';
-            break;
-        case 'lt':
-        case 'le':
-        case 'ne':
-            // Less than, less or equal, nonequal: also match negative numbers
-            return 'anyof ( header :comparator "i;ascii-casemap" :contains "'
-                . $headerstr . '" "-", ' . $code . ' )';
-            break;
-        }
-    }
-
-    /**
-     * Checks if the rule parameters are valid.
-     *
-     * @return boolean|string  True if this rule is valid, an error message
-     *                         otherwise.
-     */
-     function check()
-     {
-         $headers = preg_split('(\r\n|\n|\r)', $this->_vars['headers']);
-         return $headers ? true : _("No headers specified");
-     }
-
-    /**
-     * Returns a list of sieve extensions required for this rule and any
-     * sub-rules.
-     *
-     * @return array  A Sieve extension list.
-     */
-    function requires()
-     {
-         return array('relational', 'comparator-i;ascii-numeric');
-     }
-
-}
-
-/**
- * The Sieve_Test_Size class represents a message size test.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Sieve_Test_Size extends Sieve_Test {
-
-    /**
-     * Constructor.
-     *
-     * @param array $vars  Any required parameters.
-     */
-    function Sieve_Test_Size($vars = array())
-    {
-        $this->_vars['comparison'] = (isset($vars['comparison'])) ? $vars['comparison'] : '';
-        $this->_vars['size'] = (isset($vars['size'])) ? $vars['size'] : '';
-    }
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        return 'size ' . $this->_vars['comparison'] . ' ' . $this->_vars['size'];
-    }
-
-    /**
-     * Checks if the rule parameters are valid.
-     *
-     * @return boolean|string  True if this rule is valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        if (!(isset($this->_vars['comparison']) &&
-              isset($this->_vars['size']))) {
-            return false;
-        }
-
-        return true;
-    }
-
-}
-
-/**
- * The Sieve_Test_Not class represents the inverse of a given test.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Sieve_Test_Not extends Sieve_Test {
-
-    var $_test = array();
-
-    /**
-     * Constructor.
-     *
-     * @param Sieve_Test $test  A Sieve_Test object.
-     */
-    function Sieve_Test_Not($test)
-    {
-        $this->_test = $test;
-    }
-
-    /**
-     * Checks if the sub-rule is valid.
-     *
-     * @return boolean|string  True if this rule is valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        return $this->_test->check();
-    }
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        return 'not ' . $this->_test->toCode();
-    }
-
-    /**
-     * Returns a list of sieve extensions required for this rule and any
-     * sub-rules.
-     *
-     * @return array  A Sieve extension list.
-     */
-    function requires()
-    {
-        return $this->_test->requires();
-    }
-
-}
-
-/**
- * The Sieve_Test_Exists class represents a test for the existsance of one or
- * more headers in a message.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Sieve_Test_Exists extends Sieve_Test {
-
-    /**
-     * Constructor.
-     *
-     * @param array $vars  Any required parameters.
-     */
-    function Sieve_Test_Exists($vars = array())
-    {
-        $this->_vars['headers'] = (isset($vars['headers'])) ? $vars['headers'] : '';
-    }
-
-    /**
-     * Checks if the rule parameters are valid.
-     *
-     * @return boolean|string  True if this rule is valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        $headers = preg_split('(\r\n|\n|\r)', $this->_vars['headers']);
-        if (!$headers) {
-            return _("No headers specified");
-        }
-
-        return true;
-    }
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        $code = 'exists ';
-        $headers = preg_split('(\r\n|\n|\r)', $this->_vars['headers']);
-        if (count($headers) > 1) {
-            $code .= "[";
-            $headerstr = '';
-            foreach ($headers as $header) {
-                $headerstr .= (empty($headerstr) ? '"' : ', "') .
-                    Ingo_Script_Sieve::escapeString($header) . '"';
-            }
-            $code .= $headerstr . "] ";
-        } elseif (count($headers) == 1) {
-            $code .= '"' . Ingo_Script_Sieve::escapeString($headers[0]) . '" ';
-        } else {
-            return "**error** No Headers Specified";
-        }
-
-        return $code;
-    }
-
-}
-
-/**
- * The Sieve_Test_Address class represents a test on parts or all of the
- * addresses in the given fields.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Sieve_Test_Address extends Sieve_Test {
-
-    /**
-     * Constructor.
-     *
-     * @param array $vars  Any required parameters.
-     */
-    function Sieve_Test_Address($vars)
-    {
-        $this->_vars['headers'] = (isset($vars['headers'])) ? $vars['headers'] : '';
-        $this->_vars['comparator'] = (isset($vars['comparator'])) ? $vars['comparator'] : 'i;ascii-casemap';
-        $this->_vars['match-type'] = (isset($vars['match-type'])) ? $vars['match-type'] : ':is';
-        $this->_vars['address-part'] = (isset($vars['address-part'])) ? $vars['address-part'] : ':all';
-        $this->_vars['addresses'] = (isset($vars['addresses'])) ? $vars['addresses'] : '';
-    }
-
-    /**
-     * Checks if the rule parameters are valid.
-     *
-     * @return boolean|string  True if this rule is valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        $headers = preg_split('(\r\n|\n|\r)', $this->_vars['headers']);
-        if (!$headers) {
-            return false;
-        }
-
-        $addresses = preg_split('(\r\n|\n|\r)', $this->_vars['addresses']);
-        if (!$addresses) {
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        $code = 'address ' .
-            $this->_vars['address-part'] . ' ' .
-            ':comparator "' . $this->_vars['comparator'] . '" ' .
-            $this->_vars['match-type'] . ' ';
-
-        $headers = preg_split('(\r\n|\n|\r|,)', $this->_vars['headers']);
-        $headers = array_filter($headers);
-        if (count($headers) > 1) {
-            $code .= "[";
-            $headerstr = '';
-            foreach ($headers as $header) {
-                $header = trim($header);
-                if (!empty($header)) {
-                    $headerstr .= empty($headerstr) ? '"' : ', "';
-                    $headerstr .= Ingo_Script_Sieve::escapeString($header, $this->_vars['match-type'] == ':regex') . '"';
-                }
-            }
-            $code .= $headerstr . "] ";
-        } elseif (count($headers) == 1) {
-            $code .= '"' . Ingo_Script_Sieve::escapeString($headers[0], $this->_vars['match-type'] == ':regex') . '" ';
-        } else {
-            return "No Headers Specified";
-        }
-
-        $addresses = preg_split('(\r\n|\n|\r)', $this->_vars['addresses']);
-        $addresses = array_filter($addresses);
-        if (count($addresses) > 1) {
-            $code .= "[";
-            $addressstr = '';
-            foreach ($addresses as $addr) {
-                $addr = trim($addr);
-                if (!empty($addr)) {
-                    $addressstr .= empty($addressstr) ? '"' : ', "';
-                    $addressstr .= Ingo_Script_Sieve::escapeString($addr, $this->_vars['match-type'] == ':regex') . '"';
-                }
-            }
-            $code .= $addressstr . "] ";
-        } elseif (count($addresses) == 1) {
-            $code .= '"' . Ingo_Script_Sieve::escapeString($addresses[0], $this->_vars['match-type'] == ':regex') . '" ';
-        } else {
-            return "No Addresses Specified";
-        }
-
-        return $code;
-    }
-
-    /**
-     * Returns a list of sieve extensions required for this rule and any
-     * sub-rules.
-     *
-     * @return array  A Sieve extension list.
-     */
-    function requires()
-    {
-        if ($this->_vars['match-type'] == ':regex') {
-            return array('regex');
-        }
-        return array();
-    }
-
-}
-
-/**
- * The Sieve_Test_Header class represents a test on the contents of one or
- * more headers in a message.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Sieve_Test_Header extends Sieve_Test {
-
-    /**
-     * Constructor.
-     *
-     * @param array $vars  Any required parameters.
-     */
-    function Sieve_Test_Header($vars = array())
-    {
-        $this->_vars['headers'] = isset($vars['headers'])
-            ? $vars['headers']
-            : 'Subject';
-        $this->_vars['comparator'] = isset($vars['comparator'])
-            ? $vars['comparator']
-            : 'i;ascii-casemap';
-        $this->_vars['match-type'] = isset($vars['match-type'])
-            ? $vars['match-type']
-            : ':is';
-        $this->_vars['strings'] = isset($vars['strings'])
-            ? $vars['strings']
-            : '';
-    }
-
-    /**
-     * Checks if the rule parameters are valid.
-     *
-     * @return boolean|string  True if this rule is valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        $headers = preg_split('((?<!\\\)\,|\r\n|\n|\r)', $this->_vars['headers']);
-        if (!$headers) {
-            return false;
-        }
-
-        $strings = preg_split('((?<!\\\)\,|\r\n|\n|\r)', $this->_vars['strings']);
-        if (!$strings) {
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        $code = 'header ' .
-            ':comparator "' . $this->_vars['comparator'] . '" ' .
-            $this->_vars['match-type'] . ' ';
-
-        $headers = preg_split('(\r\n|\n|\r)', $this->_vars['headers']);
-        $headers = array_filter($headers);
-        if (count($headers) > 1) {
-            $code .= "[";
-            $headerstr = '';
-            foreach ($headers as $header) {
-                $headerstr .= empty($headerstr) ? '"' : ', "';
-                $headerstr .= Ingo_Script_Sieve::escapeString($header, $this->_vars['match-type'] == ':regex') . '"';
-            }
-            $code .= $headerstr . "] ";
-        } elseif (count($headers) == 1) {
-            $code .= '"' . $headers[0] . '" ';
-        } else {
-            return _("No headers specified");
-        }
-
-        $strings = preg_split('(\r\n|\n|\r)', $this->_vars['strings']);
-        $strings = array_filter($strings);
-        if (count($strings) > 1) {
-            $code .= "[";
-            $stringlist = '';
-            foreach ($strings as $str) {
-                $stringlist .= empty($stringlist) ? '"' : ', "';
-                $stringlist .= Ingo_Script_Sieve::escapeString($str, $this->_vars['match-type'] == ':regex') . '"';
-            }
-            $code .= $stringlist . "] ";
-        } elseif (count($strings) == 1) {
-            $code .= '"' . Ingo_Script_Sieve::escapeString(reset($strings), $this->_vars['match-type'] == ':regex') . '" ';
-        } else {
-            return _("No strings specified");
-        }
-
-        return $code;
-    }
-
-    /**
-     * Returns a list of sieve extensions required for this rule and any
-     * sub-rules.
-     *
-     * @return array  A Sieve extension list.
-     */
-    function requires()
-    {
-        if ($this->_vars['match-type'] == ':regex') {
-            return array('regex');
-        }
-        return array();
-    }
-
-}
-
-/**
- * The Sieve_Test_Body class represents a test on the contents of the body in
- * a message.
- *
- * @author  Michael Menge <michael.menge@zdv.uni-tuebingen.de>
- * @package Ingo
- */
-class Sieve_Test_Body extends Sieve_Test {
-
-    /**
-     * Constructor.
-     *
-     * @param array $vars  Any required parameters.
-     */
-    function Sieve_Test_Body($vars = array())
-    {
-        $this->_vars['comparator'] = (isset($vars['comparator'])) ? $vars['comparator'] : 'i;ascii-casemap';
-        $this->_vars['match-type'] = (isset($vars['match-type'])) ? $vars['match-type'] : ':is';
-        $this->_vars['strings'] = (isset($vars['strings'])) ? $vars['strings'] : '';
-    }
-
-    /**
-     * Checks if the rule parameters are valid.
-     *
-     * @return boolean|string  True if this rule is valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        $strings = preg_split('((?<!\\\)\,|\r\n|\n|\r)', $this->_vars['strings']);
-        if (!$strings) {
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        $code = 'body ' .
-            ':comparator "' . $this->_vars['comparator'] . '" ' .
-            $this->_vars['match-type'] . ' ';
-
-        $strings = preg_split('(\r\n|\n|\r)', $this->_vars['strings']);
-        $strings = array_filter($strings);
-        if (count($strings) > 1) {
-            $code .= "[";
-            $stringlist = '';
-            foreach ($strings as $str) {
-                $stringlist .= empty($stringlist) ? '"' : ', "';
-                $stringlist .= Ingo_Script_Sieve::escapeString($str, $this->_vars['match-type'] == ':regex') . '"';
-            }
-            $code .= $stringlist . "] ";
-        } elseif (count($strings) == 1) {
-            $code .= '"' . Ingo_Script_Sieve::escapeString($strings[0], $this->_vars['match-type'] == ':regex') . '" ';
-        } else {
-            return _("No strings specified");
-        }
-
-        return $code;
-    }
-
-    /**
-     * Returns a list of sieve extensions required for this rule and any
-     * sub-rules.
-     *
-     * @return array  A Sieve extension list.
-     */
-    function requires()
-    {
-        if ($this->_vars['match-type'] == ':regex') {
-            return array('regex', 'body');
-        }
-
-        return array('body');
-    }
-
-}
-
-/**
- * A Comment.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- * @todo    This and Sieve_If should really extends a Sieve_Block eventually.
- */
-class Sieve_Comment {
-
-    var $_comment;
-
-    /**
-     * Constructor.
-     *
-     * @param string $comment  The comment text.
-     */
-    function Sieve_Comment($comment)
-    {
-        $this->_comment = $comment;
-    }
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        $code = '';
-        $lines = preg_split('(\r\n|\n|\r)', $this->_comment);
-        foreach ($lines as $line) {
-            $line = trim($line);
-            if (strlen($line)) {
-                $code .= (empty($code) ? '' : "\n") . '# ' . $line;
-            }
-        }
-        return $code;
-    }
-
-    /**
-     * Checks if the rule parameters are valid.
-     *
-     * @return boolean|string  True if this rule is valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        return true;
-    }
-
-    /**
-     * Returns a list of sieve extensions required for this rule and any
-     * sub-rules.
-     *
-     * @return array  A Sieve extension list.
-     */
-    function requires()
-    {
-        return array();
-    }
-
-}
-
-/**
- * The Sieve_Action class represents an action in a Sieve script.
- *
- * An action is anything that has a side effect eg: discard, redirect.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Sieve_Action {
-
-    /**
-     * Any necessary action parameters.
-     *
-     * @var array
-     */
-    var $_vars = array();
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        return 'toCode() Function Not Implemented in class ' . get_class($this) ;
-    }
-
-    function toString()
-    {
-        return $this->toCode();
-    }
-
-    /**
-     * Checks if the rule parameters are valid.
-     *
-     * @return boolean|string  True if this rule is valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        return 'check() Function Not Implemented in class ' . get_class($this) ;
-    }
-
-    /**
-     * Returns a list of sieve extensions required for this rule and any
-     * sub-rules.
-     *
-     * @return array  A Sieve extension list.
-     */
-    function requires()
-    {
-        return array();
-    }
-
-}
-
-/**
- * The Sieve_Action_Redirect class represents a redirect action.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Sieve_Action_Redirect extends Sieve_Action {
-
-    /**
-     * Constructor.
-     *
-     * @param array $vars  Any required parameters.
-     */
-    function Sieve_Action_Redirect($vars = array())
-    {
-        $this->_vars['address'] = (isset($vars['address'])) ? $vars['address'] : '';
-    }
-
-    function toCode($depth = 0)
-    {
-        return str_repeat(' ', $depth * 4) . 'redirect ' .
-            '"' . Ingo_Script_Sieve::escapeString($this->_vars['address']) . '";';
-    }
-
-    /**
-     * Checks if the rule parameters are valid.
-     *
-     * @return boolean|string  True if this rule is valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        if (empty($this->_vars['address'])) {
-            return _("Missing address to redirect message to");
-        }
-
-        return true;
-    }
-
-}
-
-/**
- * The Sieve_Action_Reject class represents a reject action.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Sieve_Action_Reject extends Sieve_Action {
-
-    /**
-     * Constructor.
-     *
-     * @param array $vars  Any required parameters.
-     */
-    function Sieve_Action_Reject($vars = array())
-    {
-        $this->_vars['reason'] = (isset($vars['reason'])) ? $vars['reason'] : '';
-    }
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        return 'reject "' . Ingo_Script_Sieve::escapeString($this->_vars['reason']) . '";';
-    }
-
-    /**
-     * Checks if the rule parameters are valid.
-     *
-     * @return boolean|string  True if this rule is valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        if (empty($this->_vars['reason'])) {
-            return _("Missing reason for reject");
-        }
-
-        return true;
-    }
-
-    /**
-     * Returns a list of sieve extensions required for this rule and any
-     * sub-rules.
-     *
-     * @return array  A Sieve extension list.
-     */
-    function requires()
-    {
-        return array('reject');
-    }
-
-}
-
-/**
- * The Sieve_Action_Keep class represents a keep action.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Sieve_Action_Keep extends Sieve_Action {
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        return 'keep;';
-    }
-
-    /**
-     * Checks if the rule parameters are valid.
-     *
-     * @return boolean|string  True if this rule is valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        return true;
-    }
-
-}
-
-/**
- * The Sieve_Action_Discard class represents a discard action.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Sieve_Action_Discard extends Sieve_Action {
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        return 'discard;';
-    }
-
-    /**
-     * Checks if the rule parameters are valid.
-     *
-     * @return boolean|string  True if this rule is valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        return true;
-    }
-
-}
-
-/**
- * The Sieve_Action_Stop class represents a stop action.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Sieve_Action_Stop extends Sieve_Action {
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        return 'stop;';
-    }
-
-    /**
-     * Checks if the rule parameters are valid.
-     *
-     * @return boolean|string  True if this rule is valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        return true;
-    }
-
-}
-
-/**
- * The Sieve_Action_Fileinto class represents a fileinto action.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Sieve_Action_Fileinto extends Sieve_Action {
-
-    /**
-     * Constructor.
-     *
-     * @param array $vars  Any required parameters.
-     */
-    function Sieve_Action_Fileinto($vars = array())
-    {
-        $this->_vars['folder'] = (isset($vars['folder'])) ? $vars['folder'] : '';
-        if (!empty($vars['utf8'])) {
-            $this->_vars['folder'] = String::convertCharset($this->_vars['folder'], 'UTF7-IMAP', 'UTF-8');
-        }
-    }
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        return 'fileinto "' . Ingo_Script_Sieve::escapeString($this->_vars['folder']) . '";';
-    }
-
-    /**
-     * Checks if the rule parameters are valid.
-     *
-     * @return boolean|string  True if this rule is valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        if (empty($this->_vars['folder'])) {
-            return _("Inexistant mailbox specified for message delivery.");
-        }
-
-        return true;
-    }
-
-    /**
-     * Returns a list of sieve extensions required for this rule and any
-     * sub-rules.
-     *
-     * @return array  A Sieve extension list.
-     */
-    function requires()
-    {
-        return array('fileinto');
-    }
-
-}
-
-/**
- * The Sieve_Action_Vacation class represents a vacation action.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Sieve_Action_Vacation extends Sieve_Action {
-
-    /**
-     * Constructor.
-     *
-     * @param array $vars  Any required parameters.
-     */
-    function Sieve_Action_Vacation($vars = array())
-    {
-        $this->_vars['days'] = isset($vars['days']) ? intval($vars['days']) : '';
-        $this->_vars['addresses'] = isset($vars['addresses']) ? $vars['addresses'] : '';
-        $this->_vars['subject'] = isset($vars['subject']) ? $vars['subject'] : '';
-        $this->_vars['reason'] = isset($vars['reason']) ? $vars['reason'] : '';
-        $this->_vars['start'] = isset($vars['start']) ? $vars['start'] : '';
-        $this->_vars['start_year'] = isset($vars['start_year']) ? $vars['start_year'] : '';
-        $this->_vars['start_month'] = isset($vars['start_month']) ? $vars['start_month'] : '';
-        $this->_vars['start_day'] = isset($vars['start_day']) ? $vars['start_day'] : '';
-        $this->_vars['end'] = isset($vars['end']) ? $vars['end'] : '';
-        $this->_vars['end_year'] = isset($vars['end_year']) ? $vars['end_year'] : '';
-        $this->_vars['end_month'] = isset($vars['end_month']) ? $vars['end_month'] : '';
-        $this->_vars['end_day'] = isset($vars['end_day']) ? $vars['end_day'] : '';
-    }
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        $start_year = $this->_vars['start_year'];
-        $start_month = $this->_vars['start_month'];
-        $start_day = $this->_vars['start_day'];
-
-        $end_year = $this->_vars['end_year'];
-        $end_month = $this->_vars['end_month'];
-        $end_day = $this->_vars['end_day'];
-
-        $code = '';
-
-        if (empty($this->_vars['start']) || empty($this->_vars['end'])) {
-            return $this->_vacationCode();
-        } elseif ($end_year > $start_year + 1) {
-            $code .= $this->_yearCheck($start_year + 1, $end_year - 1)
-                . $this->_vacationCode()
-                . "\n}\n"
-                . $this->_yearCheck($start_year, $start_year);
-            if ($start_month < 12) {
-                $code .= $this->_monthCheck($start_month + 1, 12)
-                    . $this->_vacationCode()
-                    . "\n}\n";
-            }
-            $code .= $this->_monthCheck($start_month, $start_month)
-                . $this->_dayCheck($start_day, 31)
-                . $this->_vacationCode()
-                . "\n}\n}\n}\n"
-                . $this->_yearCheck($end_year, $end_year);
-            if ($end_month > 1) {
-                $code .= $this->_monthCheck(1, $end_month - 1)
-                    . $this->_vacationCode()
-                    . "\n}\n";
-            }
-            $code .= $this->_monthCheck($end_month, $end_month)
-                . $this->_dayCheck(1, $end_day)
-                . $this->_vacationCode()
-                . "\n}\n}\n}\n";
-        } elseif ($end_year == $start_year + 1) {
-            $code .= $this->_yearCheck($start_year, $start_year);
-            if ($start_month < 12) {
-                $code .= $this->_monthCheck($start_month + 1, 12)
-                    . $this->_vacationCode()
-                    . "\n}\n";
-            }
-            $code .= $this->_monthCheck($start_month, $start_month)
-                . $this->_dayCheck($start_day, 31)
-                . $this->_vacationCode()
-                . "\n}\n}\n}\n"
-                . $this->_yearCheck($end_year, $end_year);
-            if ($end_month > 1) {
-                $code .= $this->_monthCheck(1, $end_month - 1)
-                    . $this->_vacationCode()
-                    . "\n}\n";
-            }
-            $code .= $this->_monthCheck($end_month, $end_month)
-                . $this->_dayCheck(1, $end_day)
-                . $this->_vacationCode()
-                . "\n}\n}\n}\n";
-        } elseif ($end_year == $start_year) {
-            $code .= $this->_yearCheck($start_year, $start_year);
-            if ($end_month > $start_month) {
-                if ($end_month > $start_month + 1) {
-                    $code .= $this->_monthCheck($start_month + 1, $end_month - 1)
-                        . $this->_vacationCode()
-                        . "\n}\n";
-                }
-                $code .= $this->_monthCheck($start_month, $start_month)
-                    . $this->_dayCheck($start_day, 31)
-                    . $this->_vacationCode()
-                    . "\n}\n}\n"
-                    . $this->_monthCheck($end_month, $end_month)
-                    . $this->_dayCheck(1, $end_day)
-                    . $this->_vacationCode()
-                    . "\n}\n}\n";
-            } elseif ($end_month == $start_month) {
-                $code .= $this->_monthCheck($start_month, $start_month)
-                    . $this->_dayCheck($start_day, $end_day)
-                    . $this->_vacationCode()
-                    . "\n}\n}\n";
-            }
-            $code .= "}\n";
-        }
-
-        return $code;
-    }
-
-    /**
-     * Checks if the rule parameters are valid.
-     *
-     * @return boolean|string  True if this rule is valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        if (empty($this->_vars['reason'])) {
-            return _("Missing reason in vacation.");
-        }
-
-        return true;
-    }
-
-    /**
-     * Returns a list of sieve extensions required for this rule and any
-     * sub-rules.
-     *
-     * @return array  A Sieve extension list.
-     */
-    function requires()
-    {
-        return array('vacation', 'regex');
-    }
-
-    /**
-     */
-    function _vacationCode()
-    {
-        $code = 'vacation :days ' . $this->_vars['days'] . ' ';
-        $addresses = $this->_vars['addresses'];
-        $stringlist = '';
-        if (count($addresses) > 1) {
-            foreach ($addresses as $address) {
-                $address = trim($address);
-                if (!empty($address)) {
-                    $stringlist .= empty($stringlist) ? '"' : ', "';
-                    $stringlist .= Ingo_Script_Sieve::escapeString($address) . '"';
-                }
-            }
-            $stringlist = "[" . $stringlist . "] ";
-        } elseif (count($addresses) == 1) {
-            $stringlist = '"' . Ingo_Script_Sieve::escapeString($addresses[0]) . '" ';
-        }
-
-        if (!empty($stringlist)) {
-            $code .= ':addresses ' . $stringlist;
-        }
-
-        if (!empty($this->_vars['subject'])) {
-            $code .= ':subject "' . Horde_Mime::encode(Ingo_Script_Sieve::escapeString($this->_vars['subject']), 'UTF-8') . '" ';
-        }
-        return $code
-            . '"' . Ingo_Script_Sieve::escapeString($this->_vars['reason'])
-            . '";';
-    }
-
-    /**
-     */
-    function _yearCheck($begin, $end)
-    {
-        $code = 'if header :regex "Received" "^.*(' . $begin;
-        for ($i = $begin + 1; $i <= $end; $i++) {
-            $code .= '|' . $i;
-        }
-        return $code
-            . ') (\\\\(.*\\\\) )?..:..:.. (\\\\(.*\\\\) )?((\\\\+|\\\\-)[[:digit:]]{4}|.{1,5})( \\\\(.*\\\\))?$" {'
-            . "\n    ";
-    }
-
-    /**
-     */
-    function _monthCheck($begin, $end)
-    {
-        $months = array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
-                        'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
-        $code = 'if header :regex "Received" "^.*(' . $months[$begin - 1];
-        for ($i = $begin + 1; $i <= $end; $i++) {
-            $code .= '|' . $months[$i - 1];
-        }
-        return $code
-            . ') (\\\\(.*\\\\) )?.... (\\\\(.*\\\\) )?..:..:.. (\\\\(.*\\\\) )?((\\\\+|\\\\-)[[:digit:]]{4}|.{1,5})( \\\\(.*\\\\))?$" {'
-            . "\n    ";
-    }
-
-    /**
-     */
-    function _dayCheck($begin, $end)
-    {
-        $code = 'if header :regex "Received" "^.*(' . str_repeat('[0 ]', 2 - strlen($begin)) . $begin;
-        for ($i = $begin + 1; $i <= $end; $i++) {
-            $code .= '|' . str_repeat('[0 ]', 2 - strlen($i)) . $i;
-        }
-        return $code
-            . ') (\\\\(.*\\\\) )?... (\\\\(.*\\\\) )?.... (\\\\(.*\\\\) )?..:..:.. (\\\\(.*\\\\) )?((\\\\+|\\\\-)[[:digit:]]{4}|.{1,5})( \\\\(.*\\\\))?$" {'
-            . "\n    ";
-    }
-
-}
-
-/**
- * The Sieve_Action_Flag class is the base class for flag actions.
- *
- * @author  Michael Slusarz <slusarz@horde.org>
- * @package Ingo
- */
-class Sieve_Action_Flag extends Sieve_Action {
-
-    /**
-     * Constructor.
-     *
-     * @params array $vars  Any required parameters.
-     */
-    function Sieve_Action_Flag($vars = array())
-    {
-        if (isset($vars['flags'])) {
-            if ($vars['flags'] & Ingo_Storage::FLAG_ANSWERED) {
-                $this->_vars['flags'][] = '\Answered';
-            }
-            if ($vars['flags'] & Ingo_Storage::FLAG_DELETED) {
-                $this->_vars['flags'][] = '\Deleted';
-            }
-            if ($vars['flags'] & Ingo_Storage::FLAG_FLAGGED) {
-                $this->_vars['flags'][] = '\Flagged';
-            }
-            if ($vars['flags'] & Ingo_Storage::FLAG_SEEN) {
-                $this->_vars['flags'][] = '\Seen';
-            }
-        } else {
-            $this->_vars['flags'] = '';
-        }
-    }
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @param string $mode  The sieve flag command to use. Either 'removeflag'
-     *                      or 'addflag'.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function _toCode($mode)
-    {
-        $code  = '';
-
-        if (is_array($this->_vars['flags']) && !empty($this->_vars['flags'])) {
-            $code .= $mode . ' ';
-            if (count($this->_vars['flags']) > 1) {
-                $stringlist = '';
-                foreach ($this->_vars['flags'] as $flag) {
-                    $flag = trim($flag);
-                    if (!empty($flag)) {
-                        $stringlist .= empty($stringlist) ? '"' : ', "';
-                        $stringlist .= Ingo_Script_Sieve::escapeString($flag) . '"';
-                    }
-                }
-                $stringlist = '[' . $stringlist . ']';
-                $code .= $stringlist . ';';
-            } else {
-                $code .= '"' . Ingo_Script_Sieve::escapeString($this->_vars['flags'][0]) . '";';
-            }
-        }
-        return $code;
-    }
-
-    /**
-     * Checks if the rule parameters are valid.
-     *
-     * @return boolean|string  True if this rule is valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        return true;
-    }
-
-    /**
-     * Returns a list of sieve extensions required for this rule and any
-     * sub-rules.
-     *
-     * @return array  A Sieve extension list.
-     */
-    function requires()
-    {
-        return array('imapflags');
-    }
-
-}
-
-/**
- * The Sieve_Action_Addflag class represents an add flag action.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Sieve_Action_Addflag extends Sieve_Action_Flag {
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        return $this->_toCode('addflag');
-    }
-
-}
-
-/**
- * The Sieve_Action_Removeflag class represents a remove flag action.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @package Ingo
- */
-class Sieve_Action_Removeflag extends Sieve_Action_Flag {
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        return $this->_toCode('removeflag');
-    }
-
-}
-
-/**
- * The Sieve_Action_Notify class represents a notify action.
- *
- * @author  Paul Wolstenholme <wolstena@sfu.ca>
- * @package Ingo
- */
-class Sieve_Action_Notify extends Sieve_Action {
-
-    /**
-     * Constructor.
-     *
-     * @param array $vars  Any required parameters.
-     */
-    function Sieve_Action_Notify($vars = array())
-    {
-        $this->_vars['address'] = isset($vars['address']) ? $vars['address'] : '';
-        $this->_vars['name'] = isset($vars['name']) ? $vars['name'] : '';
-    }
-
-    /**
-     * Returns a script snippet representing this rule and any sub-rules.
-     *
-     * @return string  A Sieve script snippet.
-     */
-    function toCode()
-    {
-        return 'notify :method "mailto" :options "' .
-            Ingo_Script_Sieve::escapeString($this->_vars['address']) .
-            '" :message "' .
-            _("You have received a new message") . "\n" .
-            _("From:") . " \$from\$ \n" .
-            _("Subject:") . " \$subject\$ \n" .
-            _("Rule:") . ' ' . $this->_vars['name'] . '";';
-    }
-
-    /**
-     * Checks if the rule parameters are valid.
-     *
-     * @return boolean|string  True if this rule is valid, an error message
-     *                         otherwise.
-     */
-    function check()
-    {
-        if (empty($this->_vars['address'])) {
-            return _("Missing address to notify");
-        }
-
-        return true;
-    }
-
-    /**
-     * Returns a list of sieve extensions required for this rule and any
-     * sub-rules.
-     *
-     * @return array  A Sieve extension list.
-     */
-    function requires()
-    {
-        return array('notify');
-    }
-
-}
diff --git a/ingo/lib/Script/Sieve/Action.php b/ingo/lib/Script/Sieve/Action.php
new file mode 100644 (file)
index 0000000..13bbe2b
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Action class represents an action in a Sieve script.
+ *
+ * An action is anything that has a side effect eg: discard, redirect.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Action
+{
+    /**
+     * Any necessary action parameters.
+     *
+     * @var array
+     */
+    protected $_vars = array();
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        return 'toCode() Function Not Implemented in class ' . get_class($this) ;
+    }
+
+    public function toString()
+    {
+        return $this->toCode();
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        return 'check() Function Not Implemented in class ' . get_class($this) ;
+    }
+
+    /**
+     * Returns a list of sieve extensions required for this rule and any
+     * sub-rules.
+     *
+     * @return array  A Sieve extension list.
+     */
+    public function requires()
+    {
+        return array();
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Action/Addflag.php b/ingo/lib/Script/Sieve/Action/Addflag.php
new file mode 100644 (file)
index 0000000..11fd146
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Action_Addflag class represents an add flag action.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Action_Addflag extends Ingo_Script_Sieve_Action_Flag
+{
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        return $this->_toCode('addflag');
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Action/Discard.php b/ingo/lib/Script/Sieve/Action/Discard.php
new file mode 100644 (file)
index 0000000..8cec42e
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Action_Discard class represents a discard action.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Action_Discard extends Ingo_Script_Sieve_Action
+{
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        return 'discard;';
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        return true;
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Action/Fileinto.php b/ingo/lib/Script/Sieve/Action/Fileinto.php
new file mode 100644 (file)
index 0000000..d3d7532
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Action_Fileinto class represents a fileinto action.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Action_Fileinto extends Ingo_Script_Sieve_Action
+{
+    /**
+     * Constructor.
+     *
+     * @param array $vars  Any required parameters.
+     */
+    public function __construct($vars = array())
+    {
+        $this->_vars['folder'] = isset($vars['folder'])
+            ? $vars['folder']
+            : '';
+
+        if (!empty($vars['utf8'])) {
+            $this->_vars['folder'] = Horde_String::convertCharset($this->_vars['folder'], 'UTF7-IMAP', 'UTF-8');
+        }
+    }
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        return 'fileinto "' . Ingo_Script_Sieve::escapeString($this->_vars['folder']) . '";';
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        return empty($this->_vars['folder'])
+            ? _("Inexistant mailbox specified for message delivery.")
+            : true;
+    }
+
+    /**
+     * Returns a list of sieve extensions required for this rule and any
+     * sub-rules.
+     *
+     * @return array  A Sieve extension list.
+     */
+    public function requires()
+    {
+        return array('fileinto');
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Action/Flag.php b/ingo/lib/Script/Sieve/Action/Flag.php
new file mode 100644 (file)
index 0000000..b21ab89
--- /dev/null
@@ -0,0 +1,92 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Action_Flag class is the base class for flag actions.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Michael Slusarz <slusarz@horde.org>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Action_Flag extends Ingo_Script_Sieve_Action
+{
+    /**
+     * Constructor.
+     *
+     * @params array $vars  Any required parameters.
+     */
+    public function __construct($vars = array())
+    {
+        if (isset($vars['flags'])) {
+            if ($vars['flags'] & Ingo_Storage::FLAG_ANSWERED) {
+                $this->_vars['flags'][] = '\Answered';
+            }
+            if ($vars['flags'] & Ingo_Storage::FLAG_DELETED) {
+                $this->_vars['flags'][] = '\Deleted';
+            }
+            if ($vars['flags'] & Ingo_Storage::FLAG_FLAGGED) {
+                $this->_vars['flags'][] = '\Flagged';
+            }
+            if ($vars['flags'] & Ingo_Storage::FLAG_SEEN) {
+                $this->_vars['flags'][] = '\Seen';
+            }
+        } else {
+            $this->_vars['flags'] = '';
+        }
+    }
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @param string $mode  The sieve flag command to use. Either 'removeflag'
+     *                      or 'addflag'.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function _toCode($mode)
+    {
+        $code  = '';
+
+        if (is_array($this->_vars['flags']) && !empty($this->_vars['flags'])) {
+            $code .= $mode . ' ';
+            if (count($this->_vars['flags']) > 1) {
+                $stringlist = '';
+                foreach ($this->_vars['flags'] as $flag) {
+                    $flag = trim($flag);
+                    if (!empty($flag)) {
+                        $stringlist .= empty($stringlist) ? '"' : ', "';
+                        $stringlist .= Ingo_Script_Sieve::escapeString($flag) . '"';
+                    }
+                }
+                $stringlist = '[' . $stringlist . ']';
+                $code .= $stringlist . ';';
+            } else {
+                $code .= '"' . Ingo_Script_Sieve::escapeString($this->_vars['flags'][0]) . '";';
+            }
+        }
+        return $code;
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        return true;
+    }
+
+    /**
+     * Returns a list of sieve extensions required for this rule and any
+     * sub-rules.
+     *
+     * @return array  A Sieve extension list.
+     */
+    public function requires()
+    {
+        return array('imapflags');
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Action/Keep.php b/ingo/lib/Script/Sieve/Action/Keep.php
new file mode 100644 (file)
index 0000000..0096ed5
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Action_Keep class represents a keep action.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Action_Keep extends Ingo_Script_Sieve_Action
+{
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        return 'keep;';
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        return true;
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Action/Notify.php b/ingo/lib/Script/Sieve/Action/Notify.php
new file mode 100644 (file)
index 0000000..b0dc5fe
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Action_Notify class represents a notify action.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Paul Wolstenholme <wolstena@sfu.ca>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Action_Notify extends Ingo_Script_Sieve_Action
+{
+    /**
+     * Constructor.
+     *
+     * @param array $vars  Any required parameters.
+     */
+    public function __construct($vars = array())
+    {
+        $this->_vars['address'] = isset($vars['address'])
+            ? $vars['address']
+            : '';
+        $this->_vars['name'] = isset($vars['name'])
+            ? $vars['name']
+            : '';
+    }
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        return 'notify :method "mailto" :options "' .
+            Ingo_Script_Sieve::escapeString($this->_vars['address']) .
+            '" :message "' .
+            _("You have received a new message") . "\n" .
+            _("From:") . " \$from\$ \n" .
+            _("Subject:") . " \$subject\$ \n" .
+            _("Rule:") . ' ' . $this->_vars['name'] . '";';
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        return empty($this->_vars['address'])
+            ? _("Missing address to notify")
+            : true;
+    }
+
+    /**
+     * Returns a list of sieve extensions required for this rule and any
+     * sub-rules.
+     *
+     * @return array  A Sieve extension list.
+     */
+    public function requires()
+    {
+        return array('notify');
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Action/Redirect.php b/ingo/lib/Script/Sieve/Action/Redirect.php
new file mode 100644 (file)
index 0000000..dd86944
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Action_Redirect class represents a redirect action.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Action_Redirect extends Ingo_Script_Sieve_Action
+{
+    /**
+     * Constructor.
+     *
+     * @param array $vars  Any required parameters.
+     */
+    public function __construct($vars = array())
+    {
+        $this->_vars['address'] = isset($vars['address'])
+            ? $vars['address']
+            : '';
+    }
+
+    /**
+     */
+    public function toCode($depth = 0)
+    {
+        return str_repeat(' ', $depth * 4) . 'redirect ' .
+            '"' . Ingo_Script_Sieve::escapeString($this->_vars['address']) . '";';
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        return empty($this->_vars['address'])
+            ? _("Missing address to redirect message to")
+            : true;
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Action/Reject.php b/ingo/lib/Script/Sieve/Action/Reject.php
new file mode 100644 (file)
index 0000000..b387f35
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Action_Reject class represents a reject action.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Action_Reject extends Ingo_Script_Sieve_Action
+{
+    /**
+     * Constructor.
+     *
+     * @param array $vars  Any required parameters.
+     */
+    public function __construct($vars = array())
+    {
+        $this->_vars['reason'] = isset($vars['reason'])
+            ? $vars['reason']
+            : '';
+    }
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        return 'reject "' . Ingo_Script_Sieve::escapeString($this->_vars['reason']) . '";';
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        return empty($this->_vars['reason'])
+            ? _("Missing reason for reject")
+            : true;
+    }
+
+    /**
+     * Returns a list of sieve extensions required for this rule and any
+     * sub-rules.
+     *
+     * @return array  A Sieve extension list.
+     */
+    public function requires()
+    {
+        return array('reject');
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Action/Removeflag.php b/ingo/lib/Script/Sieve/Action/Removeflag.php
new file mode 100644 (file)
index 0000000..6944fdf
--- /dev/null
@@ -0,0 +1,24 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Action_Removeflag class represents a remove flag
+ * action.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Action_Removeflag extends Ingo_Script_Sieve_Action_Flag
+{
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        return $this->_toCode('removeflag');
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Action/Stop.php b/ingo/lib/Script/Sieve/Action/Stop.php
new file mode 100644 (file)
index 0000000..19c5470
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Action_Stop class represents a stop action.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Action_Stop extends Ingo_Script_Sieve_Action
+{
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        return 'stop;';
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        return true;
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Action/Vacation.php b/ingo/lib/Script/Sieve/Action/Vacation.php
new file mode 100644 (file)
index 0000000..1787b04
--- /dev/null
@@ -0,0 +1,225 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Action_Vacation class represents a vacation action.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Action_Vacation extends Ingo_Script_Sieve_Action
+{
+    /**
+     * Constructor.
+     *
+     * @param array $vars  Any required parameters.
+     */
+    public function __construct($vars = array())
+    {
+        $this->_vars = array_merge(array(
+            'days' => '',
+            'addresses' => '',
+            'subject' => '',
+            'reason' => '',
+            'start' => '',
+            'start_year' => '',
+            'start_month' => '',
+            'start_day' => '',
+            'end' => '',
+            'end_year' => '',
+            'end_month' => '',
+            'end_day' => ''
+        ), $vars);
+    }
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        $start_year = $this->_vars['start_year'];
+        $start_month = $this->_vars['start_month'];
+        $start_day = $this->_vars['start_day'];
+
+        $end_year = $this->_vars['end_year'];
+        $end_month = $this->_vars['end_month'];
+        $end_day = $this->_vars['end_day'];
+
+        $code = '';
+
+        if (empty($this->_vars['start']) || empty($this->_vars['end'])) {
+            return $this->_vacationCode();
+        } elseif ($end_year > $start_year + 1) {
+            $code .= $this->_yearCheck($start_year + 1, $end_year - 1)
+                . $this->_vacationCode()
+                . "\n}\n"
+                . $this->_yearCheck($start_year, $start_year);
+            if ($start_month < 12) {
+                $code .= $this->_monthCheck($start_month + 1, 12)
+                    . $this->_vacationCode()
+                    . "\n}\n";
+            }
+            $code .= $this->_monthCheck($start_month, $start_month)
+                . $this->_dayCheck($start_day, 31)
+                . $this->_vacationCode()
+                . "\n}\n}\n}\n"
+                . $this->_yearCheck($end_year, $end_year);
+            if ($end_month > 1) {
+                $code .= $this->_monthCheck(1, $end_month - 1)
+                    . $this->_vacationCode()
+                    . "\n}\n";
+            }
+            $code .= $this->_monthCheck($end_month, $end_month)
+                . $this->_dayCheck(1, $end_day)
+                . $this->_vacationCode()
+                . "\n}\n}\n}\n";
+        } elseif ($end_year == $start_year + 1) {
+            $code .= $this->_yearCheck($start_year, $start_year);
+            if ($start_month < 12) {
+                $code .= $this->_monthCheck($start_month + 1, 12)
+                    . $this->_vacationCode()
+                    . "\n}\n";
+            }
+            $code .= $this->_monthCheck($start_month, $start_month)
+                . $this->_dayCheck($start_day, 31)
+                . $this->_vacationCode()
+                . "\n}\n}\n}\n"
+                . $this->_yearCheck($end_year, $end_year);
+            if ($end_month > 1) {
+                $code .= $this->_monthCheck(1, $end_month - 1)
+                    . $this->_vacationCode()
+                    . "\n}\n";
+            }
+            $code .= $this->_monthCheck($end_month, $end_month)
+                . $this->_dayCheck(1, $end_day)
+                . $this->_vacationCode()
+                . "\n}\n}\n}\n";
+        } elseif ($end_year == $start_year) {
+            $code .= $this->_yearCheck($start_year, $start_year);
+            if ($end_month > $start_month) {
+                if ($end_month > $start_month + 1) {
+                    $code .= $this->_monthCheck($start_month + 1, $end_month - 1)
+                        . $this->_vacationCode()
+                        . "\n}\n";
+                }
+                $code .= $this->_monthCheck($start_month, $start_month)
+                    . $this->_dayCheck($start_day, 31)
+                    . $this->_vacationCode()
+                    . "\n}\n}\n"
+                    . $this->_monthCheck($end_month, $end_month)
+                    . $this->_dayCheck(1, $end_day)
+                    . $this->_vacationCode()
+                    . "\n}\n}\n";
+            } elseif ($end_month == $start_month) {
+                $code .= $this->_monthCheck($start_month, $start_month)
+                    . $this->_dayCheck($start_day, $end_day)
+                    . $this->_vacationCode()
+                    . "\n}\n}\n";
+            }
+            $code .= "}\n";
+        }
+
+        return $code;
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        return empty($this->_vars['reason'])
+            ? _("Missing reason in vacation.")
+            : true;
+    }
+
+    /**
+     * Returns a list of sieve extensions required for this rule and any
+     * sub-rules.
+     *
+     * @return array  A Sieve extension list.
+     */
+    public function requires()
+    {
+        return array('vacation', 'regex');
+    }
+
+    /**
+     */
+    protected function _vacationCode()
+    {
+        $code = 'vacation :days ' . $this->_vars['days'] . ' ';
+        $addresses = $this->_vars['addresses'];
+        $stringlist = '';
+        if (count($addresses) > 1) {
+            foreach ($addresses as $address) {
+                $address = trim($address);
+                if (!empty($address)) {
+                    $stringlist .= empty($stringlist) ? '"' : ', "';
+                    $stringlist .= Ingo_Script_Sieve::escapeString($address) . '"';
+                }
+            }
+            $stringlist = "[" . $stringlist . "] ";
+        } elseif (count($addresses) == 1) {
+            $stringlist = '"' . Ingo_Script_Sieve::escapeString($addresses[0]) . '" ';
+        }
+
+        if (!empty($stringlist)) {
+            $code .= ':addresses ' . $stringlist;
+        }
+
+        if (!empty($this->_vars['subject'])) {
+            $code .= ':subject "' . Horde_Mime::encode(Ingo_Script_Sieve::escapeString($this->_vars['subject']), 'UTF-8') . '" ';
+        }
+        return $code
+            . '"' . Ingo_Script_Sieve::escapeString($this->_vars['reason'])
+            . '";';
+    }
+
+    /**
+     */
+    protected function _yearCheck($begin, $end)
+    {
+        $code = 'if header :regex "Received" "^.*(' . $begin;
+        for ($i = $begin + 1; $i <= $end; $i++) {
+            $code .= '|' . $i;
+        }
+        return $code
+            . ') (\\\\(.*\\\\) )?..:..:.. (\\\\(.*\\\\) )?((\\\\+|\\\\-)[[:digit:]]{4}|.{1,5})( \\\\(.*\\\\))?$" {'
+            . "\n    ";
+    }
+
+    /**
+     */
+    protected function _monthCheck($begin, $end)
+    {
+        $months = array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
+                        'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
+        $code = 'if header :regex "Received" "^.*(' . $months[$begin - 1];
+        for ($i = $begin + 1; $i <= $end; $i++) {
+            $code .= '|' . $months[$i - 1];
+        }
+        return $code
+            . ') (\\\\(.*\\\\) )?.... (\\\\(.*\\\\) )?..:..:.. (\\\\(.*\\\\) )?((\\\\+|\\\\-)[[:digit:]]{4}|.{1,5})( \\\\(.*\\\\))?$" {'
+            . "\n    ";
+    }
+
+    /**
+     */
+    protected function _dayCheck($begin, $end)
+    {
+        $code = 'if header :regex "Received" "^.*(' . str_repeat('[0 ]', 2 - strlen($begin)) . $begin;
+        for ($i = $begin + 1; $i <= $end; $i++) {
+            $code .= '|' . str_repeat('[0 ]', 2 - strlen($i)) . $i;
+        }
+        return $code
+            . ') (\\\\(.*\\\\) )?... (\\\\(.*\\\\) )?.... (\\\\(.*\\\\) )?..:..:.. (\\\\(.*\\\\) )?((\\\\+|\\\\-)[[:digit:]]{4}|.{1,5})( \\\\(.*\\\\))?$" {'
+            . "\n    ";
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Comment.php b/ingo/lib/Script/Sieve/Comment.php
new file mode 100644 (file)
index 0000000..7d96616
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+/**
+ * A Comment.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ * @todo    This and Sieve_If should really extends a Sieve_Block eventually.
+ */
+class Ingo_Script_Sieve_Comment
+{
+    /**
+     */
+    protected $_comment;
+
+    /**
+     * Constructor.
+     *
+     * @param string $comment  The comment text.
+     */
+    public function __construct($comment)
+    {
+        $this->_comment = $comment;
+    }
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        $code = '';
+        $lines = preg_split('(\r\n|\n|\r)', $this->_comment);
+        foreach ($lines as $line) {
+            $line = trim($line);
+            if (strlen($line)) {
+                $code .= (empty($code) ? '' : "\n") . '# ' . $line;
+            }
+        }
+        return $code;
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        return true;
+    }
+
+    /**
+     * Returns a list of sieve extensions required for this rule and any
+     * sub-rules.
+     *
+     * @return array  A Sieve extension list.
+     */
+    public function requires()
+    {
+        return array();
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Else.php b/ingo/lib/Script/Sieve/Else.php
new file mode 100644 (file)
index 0000000..577c261
--- /dev/null
@@ -0,0 +1,104 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Else:: class represents a Sieve Else Statement.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Else
+{
+    /**
+     * A list of Ingo_Script_Sieve_Action objects that go into the else clause.
+     *
+     * @var array
+     */
+    protected $_actions = array();
+
+    /**
+     * Constructor.
+     *
+     * @param mixed $actions  An Ingo_Script_Sieve_Action object or a list of
+     *                        Ingo_Script_Sieve_Action objects.
+     */
+    public function __construct($actions = null)
+    {
+        if (is_array($actions)) {
+            $this->_actions = $actions;
+        } elseif (!is_null($actions)) {
+            $this->_actions[] = $actions;
+        }
+    }
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+     public function toCode()
+     {
+        if (count($this->_actions) == 0) {
+            return '';
+        }
+
+        $code = 'else' . " { \n";
+        foreach ($this->_actions as $action) {
+            $code .= '    ' . $action->toCode() . "\n";
+        }
+        $code .= "} ";
+
+        return $code;
+    }
+
+    /**
+     */
+    public function setActions($actions)
+    {
+        $this->_actions = $actions;
+    }
+
+    /**
+     */
+    public function getActions()
+    {
+        return $this->_actions;
+    }
+
+    /**
+     * Checks if all sub-rules are valid.
+     *
+     * @return boolean|string  True if all rules are valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        foreach ($this->_actions as $action) {
+            $res = $action->check();
+            if ($res !== true) {
+                return $res;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns a list of sieve extensions required for this rule and any
+     * sub-rules.
+     *
+     * @return array  A Sieve extension list.
+     */
+    public function requires()
+    {
+        $requires = array();
+
+        foreach ($this->_actions as $action) {
+            $requires = array_merge($requires, $action->requires());
+        }
+
+        return $requires;
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Elsif.php b/ingo/lib/Script/Sieve/Elsif.php
new file mode 100644 (file)
index 0000000..f1871a4
--- /dev/null
@@ -0,0 +1,124 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Elsif:: class represents a Sieve Elsif Statement.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Elsif
+{
+    /**
+     * The Ingo_Script_Sieve_Test object for the if test.
+     *
+     * @var Ingo_Script_Sieve_Test
+     */
+    protected $_test;
+
+    /**
+     * A list of Ingo_Script_Sieve_Action objects that go into the if clause.
+     *
+     * @var array
+     */
+    protected $_actions = array();
+
+    /**
+     * Constructor.
+     *
+     * @param Ingo_Script_Sieve_Test $test  A Ingo_Script_Sieve_Test object.
+     */
+    public function __construct($test = null)
+    {
+        $this->_test = is_null($test)
+            ? new Ingo_Script_Sieve_Test_False()
+            : $test;
+        $this->_actions[] = new Ingo_Script_Sieve_Action_Keep();
+    }
+
+    /**
+     */
+    public function getTest()
+    {
+        return $this->_test;
+    }
+
+    /**
+     */
+    public function setTest($test)
+    {
+        $this->_test = $test;
+    }
+
+    /**
+     */
+    public function getActions()
+    {
+        return $this->_actions;
+    }
+
+    /**
+     */
+    public function setActions($actions)
+    {
+        $this->_actions = $actions;
+    }
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        $code = 'elsif ' . $this->_test->toCode() . " { \n";
+        foreach ($this->_actions as $action) {
+            $code .= '    ' . $action->toCode() . "\n";
+        }
+        $code .= "} ";
+
+        return $code;
+    }
+
+    /**
+     * Checks if all sub-rules are valid.
+     *
+     * @return boolean|string  True if all rules are valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        $res = $this->_test->check();
+        if ($res !== true) {
+            return $res;
+        }
+
+        foreach ($this->_actions as $action) {
+            $res = $action->check();
+            if ($res !== true) {
+                return $res;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns a list of sieve extensions required for this rule and any
+     * sub-rules.
+     *
+     * @return array  A Sieve extension list.
+     */
+    public function requires()
+    {
+        $requires = array();
+
+        foreach ($this->_actions as $action) {
+            $requires = array_merge($requires, $action->requires());
+        }
+
+        return array_merge($requires, $this->_test->requires());
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/If.php b/ingo/lib/Script/Sieve/If.php
new file mode 100644 (file)
index 0000000..3bdcd19
--- /dev/null
@@ -0,0 +1,198 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_If:: class represents a Sieve If Statement.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_If
+{
+    /**
+     * The Ingo_Script_Sieve_Test object for the if test.
+     *
+     * @var Ingo_Script_Sieve_Test
+     */
+    protected $_test;
+
+    /**
+     * A list of Ingo_Script_Sieve_Action objects that go into the if clause.
+     *
+     * @var array
+     */
+    protected $_actions = array();
+
+    /**
+     * A list of Ingo_Script_Sieve_Elseif objects that create optional elsif
+     * clauses.
+     *
+     * @var array
+     */
+    protected $_elsifs = array();
+
+    /**
+     * A Ingo_Script_Sieve_Else object that creates an optional else clause.
+     *
+     * @var Ingo_Script_Sieve_Else
+     */
+    protected $_else;
+
+    /**
+     * Constructor.
+     *
+     * @param Ingo_Script_Sieve_Test $test  A Ingo_Script_Sieve_Test object.
+     */
+    public function __construct($test = null)
+    {
+        $this->_test = is_null($test)
+            ? new Ingo_Script_Sieve_Test_False()
+            : $test;
+
+        $this->_actions[] = new Ingo_Script_Sieve_Action_Keep();
+        $this->_else = new Ingo_Script_Sieve_Else();
+    }
+
+    /**
+     */
+    public function getTest()
+    {
+        return $this->_test;
+    }
+
+    /**
+     */
+    public function setTest($test)
+    {
+        $this->_test = $test;
+    }
+
+    /**
+     */
+    public function getActions()
+    {
+        return $this->_actions;
+    }
+
+    /**
+     */
+    public function setActions($actions)
+    {
+        $this->_actions = $actions;
+    }
+
+    /**
+     */
+    public function getElsifs()
+    {
+        return $this->_elsifs;
+    }
+
+    /**
+     */
+    public function setElsifs($elsifs)
+    {
+        $this->_elsifs = $elsifs;
+    }
+
+    /**
+     */
+    public function addElsif($elsif)
+    {
+        $this->_elsifs[] = $elsif;
+    }
+
+    /**
+     */
+    public function getElse()
+    {
+        return $this->_else;
+    }
+
+    /**
+     */
+    public function setElse($else)
+    {
+        $this->_else = $else;
+    }
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        $code = 'if ' . $this->_test->toCode() . " { \n";
+        foreach ($this->_actions as $action) {
+            $code .= '    ' . $action->toCode() . "\n";
+        }
+        $code .= "} ";
+
+        foreach ($this->_elsifs as $elsif) {
+            $code .= $elsif->toCode();
+        }
+
+        $code .= $this->_else->toCode();
+
+        return $code . "\n";
+    }
+
+    /**
+     * Checks if all sub-rules are valid.
+     *
+     * @return boolean|string  True if all rules are valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        $res = $this->_test->check();
+        if ($res !== true) {
+            return $res;
+        }
+
+        foreach ($this->_elsifs as $elsif) {
+            $res = $elsif->check();
+            if ($res !== true) {
+                return $res;
+            }
+        }
+
+        $res = $this->_else->check();
+        if ($res !== true) {
+            return $res;
+        }
+
+        foreach ($this->_actions as $action) {
+            $res = $action->check();
+            if ($res !== true) {
+                return $res;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns a list of sieve extensions required for this rule and any
+     * sub-rules.
+     *
+     * @return array  A Sieve extension list.
+     */
+    public function requires()
+    {
+        $requires = array();
+
+        foreach ($this->_actions as $action) {
+            $requires = array_merge($requires, $action->requires());
+        }
+
+        foreach ($this->_elsifs as $elsif) {
+            $requires = array_merge($requires, $elsif->requires());
+        }
+
+        return array_merge($requires, $this->_test->requires(), $this->_else->requires());
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Sieve.php b/ingo/lib/Script/Sieve/Sieve.php
new file mode 100644 (file)
index 0000000..8287fb5
--- /dev/null
@@ -0,0 +1,674 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Action class represents an action in a Sieve script.
+ *
+ * An action is anything that has a side effect eg: discard, redirect.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+/**
+ * The Sieve_Action_Redirect class represents a redirect action.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Sieve_Action_Redirect extends Sieve_Action {
+
+    /**
+     * Constructor.
+     *
+     * @param array $vars  Any required parameters.
+     */
+    function Sieve_Action_Redirect($vars = array())
+    {
+        $this->_vars['address'] = (isset($vars['address'])) ? $vars['address'] : '';
+    }
+
+    function toCode($depth = 0)
+    {
+        return str_repeat(' ', $depth * 4) . 'redirect ' .
+            '"' . Ingo_Script_Sieve::escapeString($this->_vars['address']) . '";';
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    function check()
+    {
+        if (empty($this->_vars['address'])) {
+            return _("Missing address to redirect message to");
+        }
+
+        return true;
+    }
+
+}
+
+/**
+ * The Sieve_Action_Reject class represents a reject action.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Sieve_Action_Reject extends Sieve_Action {
+
+    /**
+     * Constructor.
+     *
+     * @param array $vars  Any required parameters.
+     */
+    function Sieve_Action_Reject($vars = array())
+    {
+        $this->_vars['reason'] = (isset($vars['reason'])) ? $vars['reason'] : '';
+    }
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    function toCode()
+    {
+        return 'reject "' . Ingo_Script_Sieve::escapeString($this->_vars['reason']) . '";';
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    function check()
+    {
+        if (empty($this->_vars['reason'])) {
+            return _("Missing reason for reject");
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns a list of sieve extensions required for this rule and any
+     * sub-rules.
+     *
+     * @return array  A Sieve extension list.
+     */
+    function requires()
+    {
+        return array('reject');
+    }
+
+}
+
+/**
+ * The Sieve_Action_Keep class represents a keep action.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Sieve_Action_Keep extends Sieve_Action {
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    function toCode()
+    {
+        return 'keep;';
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    function check()
+    {
+        return true;
+    }
+
+}
+
+/**
+ * The Sieve_Action_Discard class represents a discard action.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Sieve_Action_Discard extends Sieve_Action {
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    function toCode()
+    {
+        return 'discard;';
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    function check()
+    {
+        return true;
+    }
+
+}
+
+/**
+ * The Sieve_Action_Stop class represents a stop action.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Sieve_Action_Stop extends Sieve_Action {
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    function toCode()
+    {
+        return 'stop;';
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    function check()
+    {
+        return true;
+    }
+
+}
+
+/**
+ * The Sieve_Action_Fileinto class represents a fileinto action.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Sieve_Action_Fileinto extends Sieve_Action {
+
+    /**
+     * Constructor.
+     *
+     * @param array $vars  Any required parameters.
+     */
+    function Sieve_Action_Fileinto($vars = array())
+    {
+        $this->_vars['folder'] = (isset($vars['folder'])) ? $vars['folder'] : '';
+        if (!empty($vars['utf8'])) {
+            $this->_vars['folder'] = String::convertCharset($this->_vars['folder'], 'UTF7-IMAP', 'UTF-8');
+        }
+    }
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    function toCode()
+    {
+        return 'fileinto "' . Ingo_Script_Sieve::escapeString($this->_vars['folder']) . '";';
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    function check()
+    {
+        if (empty($this->_vars['folder'])) {
+            return _("Inexistant mailbox specified for message delivery.");
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns a list of sieve extensions required for this rule and any
+     * sub-rules.
+     *
+     * @return array  A Sieve extension list.
+     */
+    function requires()
+    {
+        return array('fileinto');
+    }
+
+}
+
+/**
+ * The Sieve_Action_Vacation class represents a vacation action.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Sieve_Action_Vacation extends Sieve_Action {
+
+    /**
+     * Constructor.
+     *
+     * @param array $vars  Any required parameters.
+     */
+    function Sieve_Action_Vacation($vars = array())
+    {
+        $this->_vars['days'] = isset($vars['days']) ? intval($vars['days']) : '';
+        $this->_vars['addresses'] = isset($vars['addresses']) ? $vars['addresses'] : '';
+        $this->_vars['subject'] = isset($vars['subject']) ? $vars['subject'] : '';
+        $this->_vars['reason'] = isset($vars['reason']) ? $vars['reason'] : '';
+        $this->_vars['start'] = isset($vars['start']) ? $vars['start'] : '';
+        $this->_vars['start_year'] = isset($vars['start_year']) ? $vars['start_year'] : '';
+        $this->_vars['start_month'] = isset($vars['start_month']) ? $vars['start_month'] : '';
+        $this->_vars['start_day'] = isset($vars['start_day']) ? $vars['start_day'] : '';
+        $this->_vars['end'] = isset($vars['end']) ? $vars['end'] : '';
+        $this->_vars['end_year'] = isset($vars['end_year']) ? $vars['end_year'] : '';
+        $this->_vars['end_month'] = isset($vars['end_month']) ? $vars['end_month'] : '';
+        $this->_vars['end_day'] = isset($vars['end_day']) ? $vars['end_day'] : '';
+    }
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    function toCode()
+    {
+        $start_year = $this->_vars['start_year'];
+        $start_month = $this->_vars['start_month'];
+        $start_day = $this->_vars['start_day'];
+
+        $end_year = $this->_vars['end_year'];
+        $end_month = $this->_vars['end_month'];
+        $end_day = $this->_vars['end_day'];
+
+        $code = '';
+
+        if (empty($this->_vars['start']) || empty($this->_vars['end'])) {
+            return $this->_vacationCode();
+        } elseif ($end_year > $start_year + 1) {
+            $code .= $this->_yearCheck($start_year + 1, $end_year - 1)
+                . $this->_vacationCode()
+                . "\n}\n"
+                . $this->_yearCheck($start_year, $start_year);
+            if ($start_month < 12) {
+                $code .= $this->_monthCheck($start_month + 1, 12)
+                    . $this->_vacationCode()
+                    . "\n}\n";
+            }
+            $code .= $this->_monthCheck($start_month, $start_month)
+                . $this->_dayCheck($start_day, 31)
+                . $this->_vacationCode()
+                . "\n}\n}\n}\n"
+                . $this->_yearCheck($end_year, $end_year);
+            if ($end_month > 1) {
+                $code .= $this->_monthCheck(1, $end_month - 1)
+                    . $this->_vacationCode()
+                    . "\n}\n";
+            }
+            $code .= $this->_monthCheck($end_month, $end_month)
+                . $this->_dayCheck(1, $end_day)
+                . $this->_vacationCode()
+                . "\n}\n}\n}\n";
+        } elseif ($end_year == $start_year + 1) {
+            $code .= $this->_yearCheck($start_year, $start_year);
+            if ($start_month < 12) {
+                $code .= $this->_monthCheck($start_month + 1, 12)
+                    . $this->_vacationCode()
+                    . "\n}\n";
+            }
+            $code .= $this->_monthCheck($start_month, $start_month)
+                . $this->_dayCheck($start_day, 31)
+                . $this->_vacationCode()
+                . "\n}\n}\n}\n"
+                . $this->_yearCheck($end_year, $end_year);
+            if ($end_month > 1) {
+                $code .= $this->_monthCheck(1, $end_month - 1)
+                    . $this->_vacationCode()
+                    . "\n}\n";
+            }
+            $code .= $this->_monthCheck($end_month, $end_month)
+                . $this->_dayCheck(1, $end_day)
+                . $this->_vacationCode()
+                . "\n}\n}\n}\n";
+        } elseif ($end_year == $start_year) {
+            $code .= $this->_yearCheck($start_year, $start_year);
+            if ($end_month > $start_month) {
+                if ($end_month > $start_month + 1) {
+                    $code .= $this->_monthCheck($start_month + 1, $end_month - 1)
+                        . $this->_vacationCode()
+                        . "\n}\n";
+                }
+                $code .= $this->_monthCheck($start_month, $start_month)
+                    . $this->_dayCheck($start_day, 31)
+                    . $this->_vacationCode()
+                    . "\n}\n}\n"
+                    . $this->_monthCheck($end_month, $end_month)
+                    . $this->_dayCheck(1, $end_day)
+                    . $this->_vacationCode()
+                    . "\n}\n}\n";
+            } elseif ($end_month == $start_month) {
+                $code .= $this->_monthCheck($start_month, $start_month)
+                    . $this->_dayCheck($start_day, $end_day)
+                    . $this->_vacationCode()
+                    . "\n}\n}\n";
+            }
+            $code .= "}\n";
+        }
+
+        return $code;
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    function check()
+    {
+        if (empty($this->_vars['reason'])) {
+            return _("Missing reason in vacation.");
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns a list of sieve extensions required for this rule and any
+     * sub-rules.
+     *
+     * @return array  A Sieve extension list.
+     */
+    function requires()
+    {
+        return array('vacation', 'regex');
+    }
+
+    /**
+     */
+    function _vacationCode()
+    {
+        $code = 'vacation :days ' . $this->_vars['days'] . ' ';
+        $addresses = $this->_vars['addresses'];
+        $stringlist = '';
+        if (count($addresses) > 1) {
+            foreach ($addresses as $address) {
+                $address = trim($address);
+                if (!empty($address)) {
+                    $stringlist .= empty($stringlist) ? '"' : ', "';
+                    $stringlist .= Ingo_Script_Sieve::escapeString($address) . '"';
+                }
+            }
+            $stringlist = "[" . $stringlist . "] ";
+        } elseif (count($addresses) == 1) {
+            $stringlist = '"' . Ingo_Script_Sieve::escapeString($addresses[0]) . '" ';
+        }
+
+        if (!empty($stringlist)) {
+            $code .= ':addresses ' . $stringlist;
+        }
+
+        if (!empty($this->_vars['subject'])) {
+            $code .= ':subject "' . Horde_Mime::encode(Ingo_Script_Sieve::escapeString($this->_vars['subject']), 'UTF-8') . '" ';
+        }
+        return $code
+            . '"' . Ingo_Script_Sieve::escapeString($this->_vars['reason'])
+            . '";';
+    }
+
+    /**
+     */
+    function _yearCheck($begin, $end)
+    {
+        $code = 'if header :regex "Received" "^.*(' . $begin;
+        for ($i = $begin + 1; $i <= $end; $i++) {
+            $code .= '|' . $i;
+        }
+        return $code
+            . ') (\\\\(.*\\\\) )?..:..:.. (\\\\(.*\\\\) )?((\\\\+|\\\\-)[[:digit:]]{4}|.{1,5})( \\\\(.*\\\\))?$" {'
+            . "\n    ";
+    }
+
+    /**
+     */
+    function _monthCheck($begin, $end)
+    {
+        $months = array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
+                        'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
+        $code = 'if header :regex "Received" "^.*(' . $months[$begin - 1];
+        for ($i = $begin + 1; $i <= $end; $i++) {
+            $code .= '|' . $months[$i - 1];
+        }
+        return $code
+            . ') (\\\\(.*\\\\) )?.... (\\\\(.*\\\\) )?..:..:.. (\\\\(.*\\\\) )?((\\\\+|\\\\-)[[:digit:]]{4}|.{1,5})( \\\\(.*\\\\))?$" {'
+            . "\n    ";
+    }
+
+    /**
+     */
+    function _dayCheck($begin, $end)
+    {
+        $code = 'if header :regex "Received" "^.*(' . str_repeat('[0 ]', 2 - strlen($begin)) . $begin;
+        for ($i = $begin + 1; $i <= $end; $i++) {
+            $code .= '|' . str_repeat('[0 ]', 2 - strlen($i)) . $i;
+        }
+        return $code
+            . ') (\\\\(.*\\\\) )?... (\\\\(.*\\\\) )?.... (\\\\(.*\\\\) )?..:..:.. (\\\\(.*\\\\) )?((\\\\+|\\\\-)[[:digit:]]{4}|.{1,5})( \\\\(.*\\\\))?$" {'
+            . "\n    ";
+    }
+
+}
+
+/**
+ * The Sieve_Action_Flag class is the base class for flag actions.
+ *
+ * @author  Michael Slusarz <slusarz@horde.org>
+ * @package Ingo
+ */
+class Sieve_Action_Flag extends Sieve_Action {
+
+    /**
+     * Constructor.
+     *
+     * @params array $vars  Any required parameters.
+     */
+    function Sieve_Action_Flag($vars = array())
+    {
+        if (isset($vars['flags'])) {
+            if ($vars['flags'] & Ingo_Storage::FLAG_ANSWERED) {
+                $this->_vars['flags'][] = '\Answered';
+            }
+            if ($vars['flags'] & Ingo_Storage::FLAG_DELETED) {
+                $this->_vars['flags'][] = '\Deleted';
+            }
+            if ($vars['flags'] & Ingo_Storage::FLAG_FLAGGED) {
+                $this->_vars['flags'][] = '\Flagged';
+            }
+            if ($vars['flags'] & Ingo_Storage::FLAG_SEEN) {
+                $this->_vars['flags'][] = '\Seen';
+            }
+        } else {
+            $this->_vars['flags'] = '';
+        }
+    }
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @param string $mode  The sieve flag command to use. Either 'removeflag'
+     *                      or 'addflag'.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    function _toCode($mode)
+    {
+        $code  = '';
+
+        if (is_array($this->_vars['flags']) && !empty($this->_vars['flags'])) {
+            $code .= $mode . ' ';
+            if (count($this->_vars['flags']) > 1) {
+                $stringlist = '';
+                foreach ($this->_vars['flags'] as $flag) {
+                    $flag = trim($flag);
+                    if (!empty($flag)) {
+                        $stringlist .= empty($stringlist) ? '"' : ', "';
+                        $stringlist .= Ingo_Script_Sieve::escapeString($flag) . '"';
+                    }
+                }
+                $stringlist = '[' . $stringlist . ']';
+                $code .= $stringlist . ';';
+            } else {
+                $code .= '"' . Ingo_Script_Sieve::escapeString($this->_vars['flags'][0]) . '";';
+            }
+        }
+        return $code;
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    function check()
+    {
+        return true;
+    }
+
+    /**
+     * Returns a list of sieve extensions required for this rule and any
+     * sub-rules.
+     *
+     * @return array  A Sieve extension list.
+     */
+    function requires()
+    {
+        return array('imapflags');
+    }
+
+}
+
+/**
+ * The Sieve_Action_Addflag class represents an add flag action.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Sieve_Action_Addflag extends Sieve_Action_Flag {
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    function toCode()
+    {
+        return $this->_toCode('addflag');
+    }
+
+}
+
+/**
+ * The Sieve_Action_Removeflag class represents a remove flag action.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Sieve_Action_Removeflag extends Sieve_Action_Flag {
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    function toCode()
+    {
+        return $this->_toCode('removeflag');
+    }
+
+}
+
+/**
+ * The Sieve_Action_Notify class represents a notify action.
+ *
+ * @author  Paul Wolstenholme <wolstena@sfu.ca>
+ * @package Ingo
+ */
+class Sieve_Action_Notify extends Sieve_Action {
+
+    /**
+     * Constructor.
+     *
+     * @param array $vars  Any required parameters.
+     */
+    function Sieve_Action_Notify($vars = array())
+    {
+        $this->_vars['address'] = isset($vars['address']) ? $vars['address'] : '';
+        $this->_vars['name'] = isset($vars['name']) ? $vars['name'] : '';
+    }
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    function toCode()
+    {
+        return 'notify :method "mailto" :options "' .
+            Ingo_Script_Sieve::escapeString($this->_vars['address']) .
+            '" :message "' .
+            _("You have received a new message") . "\n" .
+            _("From:") . " \$from\$ \n" .
+            _("Subject:") . " \$subject\$ \n" .
+            _("Rule:") . ' ' . $this->_vars['name'] . '";';
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    function check()
+    {
+        if (empty($this->_vars['address'])) {
+            return _("Missing address to notify");
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns a list of sieve extensions required for this rule and any
+     * sub-rules.
+     *
+     * @return array  A Sieve extension list.
+     */
+    function requires()
+    {
+        return array('notify');
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Test.php b/ingo/lib/Script/Sieve/Test.php
new file mode 100644 (file)
index 0000000..7e4ed6e
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Test:: class represents a Sieve Test.
+ *
+ * A test is a piece of code that evaluates to true or false.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Test
+{
+    /**
+     * Any necessary test parameters.
+     *
+     * @var array
+     */
+    protected $_vars = array();
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        return 'toCode() Function Not Implemented in class ' . get_class($this);
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        return 'check() Function Not Implemented in class ' . get_class($this);
+    }
+
+    /**
+     * Returns a list of sieve extensions required for this rule and any
+     * sub-rules.
+     *
+     * @return array  A Sieve extension list.
+     */
+    public function requires()
+    {
+        return array();
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Test/Address.php b/ingo/lib/Script/Sieve/Test/Address.php
new file mode 100644 (file)
index 0000000..e116e80
--- /dev/null
@@ -0,0 +1,116 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Test_Address class represents a test on parts or all
+ * of the addresses in the given fields.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Test_Address extends Ingo_Script_Sieve_Test
+{
+    /**
+     * Constructor.
+     *
+     * @param array $vars  Any required parameters.
+     */
+    public function __construct($vars)
+    {
+        $this->_vars['headers'] = isset($vars['headers'])
+            ? $vars['headers']
+            : '';
+        $this->_vars['comparator'] = isset($vars['comparator'])
+            ? $vars['comparator']
+            : 'i;ascii-casemap';
+        $this->_vars['match-type'] = isset($vars['match-type'])
+            ? $vars['match-type']
+            : ':is';
+        $this->_vars['address-part'] = isset($vars['address-part'])
+            ? $vars['address-part']
+            : ':all';
+        $this->_vars['addresses'] = isset($vars['addresses'])
+            ? $vars['addresses']
+            : '';
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        return preg_split('(\r\n|\n|\r)', $this->_vars['headers']) &&
+               preg_split('(\r\n|\n|\r)', $this->_vars['addresses']);
+    }
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        $code = 'address ' .
+            $this->_vars['address-part'] . ' ' .
+            ':comparator "' . $this->_vars['comparator'] . '" ' .
+            $this->_vars['match-type'] . ' ';
+
+        $headers = preg_split('(\r\n|\n|\r|,)', $this->_vars['headers']);
+        $headers = array_filter($headers);
+        if (count($headers) > 1) {
+            $code .= "[";
+            $headerstr = '';
+            foreach ($headers as $header) {
+                $header = trim($header);
+                if (!empty($header)) {
+                    $headerstr .= empty($headerstr) ? '"' : ', "';
+                    $headerstr .= Ingo_Script_Sieve::escapeString($header, $this->_vars['match-type'] == ':regex') . '"';
+                }
+            }
+            $code .= $headerstr . "] ";
+        } elseif (count($headers) == 1) {
+            $code .= '"' . Ingo_Script_Sieve::escapeString($headers[0], $this->_vars['match-type'] == ':regex') . '" ';
+        } else {
+            return "No Headers Specified";
+        }
+
+        $addresses = preg_split('(\r\n|\n|\r)', $this->_vars['addresses']);
+        $addresses = array_filter($addresses);
+        if (count($addresses) > 1) {
+            $code .= "[";
+            $addressstr = '';
+            foreach ($addresses as $addr) {
+                $addr = trim($addr);
+                if (!empty($addr)) {
+                    $addressstr .= empty($addressstr) ? '"' : ', "';
+                    $addressstr .= Ingo_Script_Sieve::escapeString($addr, $this->_vars['match-type'] == ':regex') . '"';
+                }
+            }
+            $code .= $addressstr . "] ";
+        } elseif (count($addresses) == 1) {
+            $code .= '"' . Ingo_Script_Sieve::escapeString($addresses[0], $this->_vars['match-type'] == ':regex') . '" ';
+        } else {
+            return "No Addresses Specified";
+        }
+
+        return $code;
+    }
+
+    /**
+     * Returns a list of sieve extensions required for this rule and any
+     * sub-rules.
+     *
+     * @return array  A Sieve extension list.
+     */
+    public function requires()
+    {
+        return ($this->_vars['match-type'] == ':regex')
+            ? array('regex')
+            : array();
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Test/Allof.php b/ingo/lib/Script/Sieve/Test/Allof.php
new file mode 100644 (file)
index 0000000..f35506d
--- /dev/null
@@ -0,0 +1,107 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Test_Allof class represents a Allof test structure.
+ *
+ * Equivalent to a logical AND of all the tests it contains.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Test_Allof extends Ingo_Script_Sieve_Test
+{
+    /**
+     */
+    protected $_tests = array();
+
+    /**
+     * Constructor.
+     *
+     * @param mixed $test  A Ingo_Script_Sieve_Test object or a list of
+     *                     Ingo_Script_Sieve_Test objects.
+     */
+    public function __construct($test = null)
+    {
+        if (is_array($test)) {
+            $this->_tests = $test;
+        } elseif (!is_null($test)) {
+            $this->_tests[] = $test;
+        }
+    }
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        $code = '';
+        if (count($this->_tests) > 1) {
+            $testlist = '';
+            foreach ($this->_tests as $test) {
+                $testlist .= (empty($testlist)) ? '' : ', ';
+                $testlist .= trim($test->toCode());
+            }
+
+            $code = "allof ( $testlist )";
+        } elseif (count($this->_tests) == 1) {
+            $code = $this->_tests[0]->toCode();
+        } else {
+            return 'true';
+        }
+        return $code;
+    }
+
+    /**
+     * Checks if all sub-rules are valid.
+     *
+     * @return boolean|string  True if all rules are valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        foreach ($this->_tests as $test) {
+            $res = $test->check();
+            if ($res !== true) {
+                return $res;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     */
+    public function addTest($test)
+    {
+        $this->_tests[] = $test;
+    }
+
+    /**
+     */
+    public function getTests()
+    {
+        return $this->_tests;
+    }
+
+    /**
+     * Returns a list of sieve extensions required for this rule and any
+     * sub-rules.
+     *
+     * @return array  A Sieve extension list.
+     */
+    public function requires()
+    {
+        $requires = array();
+
+        foreach ($this->_tests as $test) {
+            $requires = array_merge($requires, $test->requires());
+        }
+
+        return $requires;
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Test/Anyof.php b/ingo/lib/Script/Sieve/Test/Anyof.php
new file mode 100644 (file)
index 0000000..9135172
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Test_Anyof class represents an Anyof test structure.
+ *
+ * Equivalent to a logical OR of all the tests it contains.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Test_Anyof extends Ingo_Script_Sieve_Test
+{
+    protected $_tests = array();
+
+    /**
+     * Constructor.
+     *
+     * @param mixed $test  An Ingo_Script_Sieve_Test object or a list of
+     *                     Ingo_Script_Sieve_Test objects.
+     */
+    public function __construct($test = null)
+    {
+        if (is_array($test)) {
+            $this->_tests = $test;
+        } elseif (!is_null($test)) {
+            $this->_tests[] = $test;
+        }
+    }
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        $testlist = '';
+        if (count($this->_tests) > 1) {
+            $testlist = '';
+            foreach ($this->_tests as $test) {
+                $testlist .= (empty($testlist)) ? '' : ', ';
+                $testlist .= trim($test->toCode());
+            }
+
+            $code = "anyof ( $testlist )";
+        } elseif (count($this->_tests) == 1) {
+            $code = $this->_tests[0]->toCode();
+        } else {
+            return 'true';
+        }
+        return $code;
+    }
+
+    /**
+     */
+    public function addTest($test)
+    {
+        $this->_tests[] = $test;
+    }
+
+    /**
+     */
+    public function getTests()
+    {
+        return $this->_tests;
+    }
+
+    /**
+     * Checks if all sub-rules are valid.
+     *
+     * @return boolean|string  True if all rules are valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        foreach ($this->_tests as $test) {
+            $res = $test->check();
+            if ($res !== true) {
+                return $res;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns a list of sieve extensions required for this rule and any
+     * sub-rules.
+     *
+     * @return array  A Sieve extension list.
+     */
+    public function requires()
+    {
+        $requires = array();
+
+        foreach ($this->_tests as $test) {
+            $requires = array_merge($requires, $test->requires());
+        }
+
+        return $requires;
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Test/Body.php b/ingo/lib/Script/Sieve/Test/Body.php
new file mode 100644 (file)
index 0000000..c2d4549
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Test_Body class represents a test on the contents of
+ * the body in a message.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Michael Menge <michael.menge@zdv.uni-tuebingen.de>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Test_Body extends Ingo_Script_Sieve_Test
+{
+    /**
+     * Constructor.
+     *
+     * @param array $vars  Any required parameters.
+     */
+    public function __construct($vars = array())
+    {
+        $this->_vars['comparator'] = isset($vars['comparator'])
+            ? $vars['comparator']
+            : 'i;ascii-casemap';
+        $this->_vars['match-type'] = isset($vars['match-type'])
+            ? $vars['match-type']
+            : ':is';
+        $this->_vars['strings'] = isset($vars['strings'])
+            ? $vars['strings']
+            : '';
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        return preg_split('((?<!\\\)\,|\r\n|\n|\r)', $this->_vars['strings']);
+    }
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        $code = 'body ' .
+            ':comparator "' . $this->_vars['comparator'] . '" ' .
+            $this->_vars['match-type'] . ' ';
+
+        $strings = preg_split('(\r\n|\n|\r)', $this->_vars['strings']);
+        $strings = array_filter($strings);
+        if (count($strings) > 1) {
+            $code .= "[";
+            $stringlist = '';
+            foreach ($strings as $str) {
+                $stringlist .= empty($stringlist) ? '"' : ', "';
+                $stringlist .= Ingo_Script_Sieve::escapeString($str, $this->_vars['match-type'] == ':regex') . '"';
+            }
+            $code .= $stringlist . "] ";
+        } elseif (count($strings) == 1) {
+            $code .= '"' . Ingo_Script_Sieve::escapeString($strings[0], $this->_vars['match-type'] == ':regex') . '" ';
+        } else {
+            return _("No strings specified");
+        }
+
+        return $code;
+    }
+
+    /**
+     * Returns a list of sieve extensions required for this rule and any
+     * sub-rules.
+     *
+     * @return array  A Sieve extension list.
+     */
+    public function requires()
+    {
+        return ($this->_vars['match-type'] == ':regex')
+            ? array('regex', 'body')
+            : array('body');
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Test/Exists.php b/ingo/lib/Script/Sieve/Test/Exists.php
new file mode 100644 (file)
index 0000000..a8c34a5
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Test_Exists class represents a test for the
+ * existence of one or more headers in a message.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Test_Exists extends Ingo_Script_Sieve_Test
+{
+    /**
+     * Constructor.
+     *
+     * @param array $vars  Any required parameters.
+     */
+    public function __construct($vars = array())
+    {
+        $this->_vars['headers'] = isset($vars['headers'])
+            ? $vars['headers']
+            : '';
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        return preg_split('(\r\n|\n|\r)', $this->_vars['headers'])
+            ? true
+            : _("No headers specified");
+    }
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        $code = 'exists ';
+        $headers = preg_split('(\r\n|\n|\r)', $this->_vars['headers']);
+        if (count($headers) > 1) {
+            $code .= "[";
+            $headerstr = '';
+            foreach ($headers as $header) {
+                $headerstr .= (empty($headerstr) ? '"' : ', "') .
+                    Ingo_Script_Sieve::escapeString($header) . '"';
+            }
+            $code .= $headerstr . "] ";
+        } elseif (count($headers) == 1) {
+            $code .= '"' . Ingo_Script_Sieve::escapeString($headers[0]) . '" ';
+        } else {
+            return "**error** No Headers Specified";
+        }
+
+        return $code;
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Test/False.php b/ingo/lib/Script/Sieve/Test/False.php
new file mode 100644 (file)
index 0000000..c997cea
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Test_False class represents a test that always
+ * evaluates to false.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Test_False extends Ingo_Script_Sieve_Test
+{
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        return 'false';
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        return true;
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Test/Header.php b/ingo/lib/Script/Sieve/Test/Header.php
new file mode 100644 (file)
index 0000000..c317218
--- /dev/null
@@ -0,0 +1,106 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Test_Header class represents a test on the contents
+ * of one or more headers in a message.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Test_Header extends Ingo_Script_Sieve_Test
+{
+    /**
+     * Constructor.
+     *
+     * @param array $vars  Any required parameters.
+     */
+    public function __construct($vars = array())
+    {
+        $this->_vars['headers'] = isset($vars['headers'])
+            ? $vars['headers']
+            : 'Subject';
+        $this->_vars['comparator'] = isset($vars['comparator'])
+            ? $vars['comparator']
+            : 'i;ascii-casemap';
+        $this->_vars['match-type'] = isset($vars['match-type'])
+            ? $vars['match-type']
+            : ':is';
+        $this->_vars['strings'] = isset($vars['strings'])
+            ? $vars['strings']
+            : '';
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        return preg_split('((?<!\\\)\,|\r\n|\n|\r)', $this->_vars['headers']) &&
+               preg_split('((?<!\\\)\,|\r\n|\n|\r)', $this->_vars['strings']);
+    }
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        $code = 'header ' .
+            ':comparator "' . $this->_vars['comparator'] . '" ' .
+            $this->_vars['match-type'] . ' ';
+
+        $headers = preg_split('(\r\n|\n|\r)', $this->_vars['headers']);
+        $headers = array_filter($headers);
+        if (count($headers) > 1) {
+            $code .= "[";
+            $headerstr = '';
+            foreach ($headers as $header) {
+                $headerstr .= empty($headerstr) ? '"' : ', "';
+                $headerstr .= Ingo_Script_Sieve::escapeString($header, $this->_vars['match-type'] == ':regex') . '"';
+            }
+            $code .= $headerstr . "] ";
+        } elseif (count($headers) == 1) {
+            $code .= '"' . $headers[0] . '" ';
+        } else {
+            return _("No headers specified");
+        }
+
+        $strings = preg_split('(\r\n|\n|\r)', $this->_vars['strings']);
+        $strings = array_filter($strings);
+        if (count($strings) > 1) {
+            $code .= "[";
+            $stringlist = '';
+            foreach ($strings as $str) {
+                $stringlist .= empty($stringlist) ? '"' : ', "';
+                $stringlist .= Ingo_Script_Sieve::escapeString($str, $this->_vars['match-type'] == ':regex') . '"';
+            }
+            $code .= $stringlist . "] ";
+        } elseif (count($strings) == 1) {
+            $code .= '"' . Ingo_Script_Sieve::escapeString(reset($strings), $this->_vars['match-type'] == ':regex') . '" ';
+        } else {
+            return _("No strings specified");
+        }
+
+        return $code;
+    }
+
+    /**
+     * Returns a list of sieve extensions required for this rule and any
+     * sub-rules.
+     *
+     * @return array  A Sieve extension list.
+     */
+    public function requires()
+    {
+        return ($this->_vars['match-type'] == ':regex')
+            ? array('regex')
+            : array();
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Test/Not.php b/ingo/lib/Script/Sieve/Test/Not.php
new file mode 100644 (file)
index 0000000..f9825cc
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Test_Not class represents the inverse of a given
+ * test.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Test_Not extends Ingo_Script_Sieve_Test
+{
+    /**
+     */
+    protected $_test = array();
+
+    /**
+     * Constructor.
+     *
+     * @param Ingo_Script_Sieve_Test $test  An Ingo_Script_Sieve_Test object.
+     */
+    public function __construct($test)
+    {
+        $this->_test = $test;
+    }
+
+    /**
+     * Checks if the sub-rule is valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        return $this->_test->check();
+    }
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        return 'not ' . $this->_test->toCode();
+    }
+
+    /**
+     * Returns a list of sieve extensions required for this rule and any
+     * sub-rules.
+     *
+     * @return array  A Sieve extension list.
+     */
+    public function requires()
+    {
+        return $this->_test->requires();
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Test/Relational.php b/ingo/lib/Script/Sieve/Test/Relational.php
new file mode 100644 (file)
index 0000000..b9b704d
--- /dev/null
@@ -0,0 +1,107 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Test_Relational class represents a relational test.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Todd Merritt <tmerritt@email.arizona.edu>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Test_Relational extends Ingo_Script_Sieve_Test
+{
+    /**
+     * Constructor.
+     *
+     * @param array $vars  Any required parameters.
+     */
+    public function __construct($vars = array())
+    {
+        $this->_vars['comparison'] = isset($vars['comparison'])
+            ? $vars['comparison']
+            : '';
+        $this->_vars['headers'] = isset($vars['headers'])
+            ? $vars['headers']
+            : '';
+        $this->_vars['value'] = isset($vars['value'])
+            ? $vars['value']
+            : 0;
+    }
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        $code = 'header :value "' .
+            $this->_vars['comparison'] . '" ' .
+            ':comparator "i;ascii-numeric" ';
+
+        $headers = preg_split('(\r\n|\n|\r)', $this->_vars['headers']);
+        $header_count = count($headers);
+
+        if ($header_count > 1) {
+            $code .= "[";
+            $headerstr = '';
+
+            foreach ($headers as $val) {
+                $headerstr .= (empty($headerstr) ? '"' : ', "') .
+                    Ingo_Script_Sieve::escapeString($val) . '"';
+            }
+
+            $code .= $headerstr . '] ';
+            $headerstr = '[' . $headerstr . ']';
+        } elseif ($header_count == 1) {
+            $code .= '"' . Ingo_Script_Sieve::escapeString($headers[0]) . '" ';
+            $headerstr = Ingo_Script_Sieve::escapeString($headers[0]);
+        }
+
+        $code .= '["' . $this->_vars['value'] . '"]';
+
+        // Add workarounds for negative numbers - works only if the comparison
+        // value is positive. Sieve doesn't support comparisons of negative
+        // numbers at all so this is the best we can do.
+        switch ($this->_vars['comparison']) {
+        case 'gt':
+        case 'ge':
+        case 'eq':
+            // Greater than, greater or equal, equal: number must be
+            // non-negative.
+            return 'allof ( not header :comparator "i;ascii-casemap" :contains "'
+                . $headerstr . '" "-", ' . $code . ' )';
+
+        case 'lt':
+        case 'le':
+        case 'ne':
+            // Less than, less or equal, nonequal: also match negative numbers
+            return 'anyof ( header :comparator "i;ascii-casemap" :contains "'
+                . $headerstr . '" "-", ' . $code . ' )';
+        }
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        $headers = preg_split('(\r\n|\n|\r)', $this->_vars['headers']);
+        return $headers ? true : _("No headers specified");
+    }
+
+    /**
+     * Returns a list of sieve extensions required for this rule and any
+     * sub-rules.
+     *
+     * @return array  A Sieve extension list.
+     */
+    public function requires()
+    {
+        return array('relational', 'comparator-i;ascii-numeric');
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Test/Size.php b/ingo/lib/Script/Sieve/Test/Size.php
new file mode 100644 (file)
index 0000000..5e1ca73
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Test_Size class represents a message size test.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Test_Size extends Ingo_Script_Sieve_Test
+{
+    /**
+     * Constructor.
+     *
+     * @param array $vars  Any required parameters.
+     */
+    public function __construct($vars = array())
+    {
+        $this->_vars['comparison'] = isset($vars['comparison'])
+            ? $vars['comparison']
+            : '';
+        $this->_vars['size'] = isset($vars['size'])
+            ? $vars['size']
+            : '';
+    }
+
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        return 'size ' . $this->_vars['comparison'] . ' ' . $this->_vars['size'];
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        if (!(isset($this->_vars['comparison']) &&
+              isset($this->_vars['size']))) {
+            return false;
+        }
+
+        return true;
+    }
+
+}
diff --git a/ingo/lib/Script/Sieve/Test/True.php b/ingo/lib/Script/Sieve/Test/True.php
new file mode 100644 (file)
index 0000000..414e608
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+/**
+ * The Ingo_Script_Sieve_Test_True class represents a test that always
+ * evaluates to true.
+ *
+ * See the enclosed file LICENSE for license information (ASL).  If you
+ * did not receive this file, see http://www.horde.org/licenses/asl.php.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Ingo
+ */
+class Ingo_Script_Sieve_Test_True extends Ingo_Script_Sieve_Test
+{
+    /**
+     * Returns a script snippet representing this rule and any sub-rules.
+     *
+     * @return string  A Sieve script snippet.
+     */
+    public function toCode()
+    {
+        return 'true';
+    }
+
+    /**
+     * Checks if the rule parameters are valid.
+     *
+     * @return boolean|string  True if this rule is valid, an error message
+     *                         otherwise.
+     */
+    public function check()
+    {
+        return true;
+    }
+
+}
diff --git a/ingo/lib/Session.php b/ingo/lib/Session.php
deleted file mode 100644 (file)
index da03b76..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-<?php
-/**
- * Functions required to start a Ingo session.
- *
- * Copyright 2004-2010 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file LICENSE for license information (ASL).  If you
- * did not receive this file, see http://www.horde.org/licenses/asl.php.
- *
- * @author  Michael Slusarz <slusarz@horde.org>
- * @package Ingo
- */
-class Ingo_Session
-{
-    /**
-     * Create an ingo session.
-     * This function should only be called once, when the user first uses
-     * Ingo in a session.
-     *
-     * Creates the $ingo session variable with the following entries:
-     * 'backend' (array) - The backend configuration to use.
-     * 'change' (integer) - The timestamp of the last time the rules were
-     *                      altered.
-     * 'storage' (array) - Used by Ingo_Storage:: for caching data.
-     * 'script_categories' (array) - The list of available categories for the
-     *                               Ingo_Script driver in use.
-     * 'script_generate' (boolean) - Is the Ingo_Script::generate() call
-     *                               available?
-     *
-     * @return boolean  True on success, false on failure.
-     */
-    static public function createSession()
-    {
-        global $prefs;
-
-        $_SESSION['ingo'] = array(
-            'change' => 0,
-            'storage' => array(),
-            /* Get the backend. */
-            'backend' => Ingo::getBackend());
-
-        /* Determine if the Ingo_Script:: generate() method is available. */
-        $ingo_script = Ingo::loadIngoScript();
-        $_SESSION['ingo']['script_generate'] = $ingo_script->generateAvailable();
-
-        /* Disable categories as specified in preferences */
-        $disabled = array();
-        if ($prefs->isLocked('blacklist')) {
-            $disabled[] = Ingo_Storage::ACTION_BLACKLIST;
-        }
-        if ($prefs->isLocked('whitelist')) {
-            $disabled[] = Ingo_Storage::ACTION_WHITELIST;
-        }
-        if ($prefs->isLocked('vacation')) {
-            $disabled[] = Ingo_Storage::ACTION_VACATION;
-        }
-        if ($prefs->isLocked('forward')) {
-            $disabled[] = Ingo_Storage::ACTION_FORWARD;
-        }
-        if ($prefs->isLocked('spam')) {
-            $disabled[] = Ingo_Storage::ACTION_SPAM;
-        }
-
-        /* Set the list of categories this driver supports. */
-        $_SESSION['ingo']['script_categories'] =
-            array_merge($ingo_script->availableActions(),
-                        array_diff($ingo_script->availableCategories(),
-                                   $disabled));
-    }
-
-}
index b8716ca..599085a 100644 (file)
@@ -131,6 +131,7 @@ class Ingo_Storage
      * @param boolean $readonly  Whether to disable any write operations.
      *
      * @return Ingo_Storage_Rule|Ingo_Storage_Filters  The specified object.
+     * @throws Ingo_Exception
      */
     public function retrieve($field, $cache = true, $readonly = false)
     {
@@ -156,8 +157,6 @@ class Ingo_Storage
     /**
      * Retrieves the specified data from the storage backend.
      *
-     * @abstract
-     *
      * @param integer $field     The field name of the desired data.
      *                           See lib/Storage.php for the available fields.
      * @param boolean $readonly  Whether to disable any write operations.
@@ -176,6 +175,7 @@ class Ingo_Storage
      * @param boolean $cache                              Cache the object?
      *
      * @return boolean  True on success.
+     * @throws Ingo_Exception
      */
     public function store(&$ob, $cache = true)
     {
@@ -209,10 +209,7 @@ class Ingo_Storage
                     break;
                 }
                 $filters->addRule(array('action' => $type, 'name' => $name));
-                $result = $this->store($filters, $cache);
-                if (is_a($result, 'PEAR_Error')) {
-                    return $result;
-                }
+                $this->store($filters, $cache);
             }
         }
 
@@ -227,8 +224,6 @@ class Ingo_Storage
     /**
      * Stores the specified data in the storage backend.
      *
-     * @abstract
-     *
      * @param Ingo_Storage_Rule|Ingo_Storage_Filters $ob  The object to store.
      *
      * @return boolean  True on success.
@@ -376,13 +371,13 @@ class Ingo_Storage
      * Removes the user data from the storage backend.
      * Stub for child class to override if it can implement.
      *
-     * @param string $user The user name to delete filters for.
+     * @param string $user  The user name to delete filters for.
      *
-     * @return mixed  True | PEAR_Error
+     * @throws Ingo_Exception
      */
-    function removeUserData($user)
+    public function removeUserData($user)
     {
-       return PEAR::raiseError(_("Removing user data is not supported with the current filter storage backend."));
+           throw new Ingo_Exception(_("Removing user data is not supported with the current filter storage backend."));
     }
 
 }
index 74f2cbe..e1aeca0 100644 (file)
@@ -21,7 +21,8 @@ class Ingo_Storage_Blacklist extends Ingo_Storage_Rule
      * @param mixed $data    The list of addresses (array or string).
      * @param boolean $sort  Sort the list?
      *
-     * @return mixed  PEAR_Error on error, true on success.
+     * @return boolean  True on success.
+     * @throws Ingo_Exception
      */
     public function setBlacklist($data, $sort = true)
     {
@@ -29,7 +30,7 @@ class Ingo_Storage_Blacklist extends Ingo_Storage_Rule
         if (!empty($GLOBALS['conf']['storage']['maxblacklist'])) {
             $addr_count = count($addr);
             if ($addr_count > $GLOBALS['conf']['storage']['maxblacklist']) {
-                return PEAR::raiseError(sprintf(_("Maximum number of blacklisted addresses exceeded (Total addresses: %s, Maximum addresses: %s).  Could not add new addresses to blacklist."), $addr_count, $GLOBALS['conf']['storage']['maxblacklist']), 'horde.error');
+                throw new Ingo_Exception(sprintf(_("Maximum number of blacklisted addresses exceeded (Total addresses: %s, Maximum addresses: %s).  Could not add new addresses to blacklist."), $addr_count, $GLOBALS['conf']['storage']['maxblacklist']), 'horde.error');
             }
         }
 
@@ -37,11 +38,15 @@ class Ingo_Storage_Blacklist extends Ingo_Storage_Rule
         return true;
     }
 
+    /**
+     */
     public function setBlacklistFolder($data)
     {
         $this->_folder = $data;
     }
 
+    /**
+     */
     public function getBlacklist()
     {
         return empty($this->_addr)
@@ -49,6 +54,8 @@ class Ingo_Storage_Blacklist extends Ingo_Storage_Rule
             : array_filter($this->_addr, array('Ingo', 'filterEmptyAddress'));
     }
 
+    /**
+     */
     public function getBlacklistFolder()
     {
         return $this->_folder;
index 8a23f48..51b2b79 100644 (file)
  */
 class Ingo_Storage_Forward extends Ingo_Storage_Rule
 {
+    /**
+     */
     protected $_addr = array();
+
+    /**
+     */
     protected $_keep = true;
+
+    /**
+     */
     protected $_obtype = Ingo_Storage::ACTION_FORWARD;
 
+    /**
+     */
     public function setForwardAddresses($data, $sort = true)
     {
         $this->_addr = $this->_addressList($data, $sort);
     }
 
+    /**
+     */
     public function setForwardKeep($data)
     {
         $this->_keep = $data;
     }
 
+    /**
+     */
     public function getForwardAddresses()
     {
         if (is_array($this->_addr)) {
@@ -37,6 +51,8 @@ class Ingo_Storage_Forward extends Ingo_Storage_Rule
         return $this->_addr;
     }
 
+    /**
+     */
     public function getForwardKeep()
     {
         return $this->_keep;
index 373fb07..b046c89 100644 (file)
 
 class Ingo_Storage_Mock extends Ingo_Storage
 {
+    /**
+     */
     protected $_data = array();
 
+    /**
+     */
     protected function _retrieve($field)
     {
         if (empty($this->_data[$field])) {
@@ -47,6 +51,8 @@ class Ingo_Storage_Mock extends Ingo_Storage
         return $this->_data[$field];
     }
 
+    /**
+     */
     protected function _store(&$ob)
     {
         $this->_data[$ob->obType()] = $ob;
index 9dfb0e7..bbf3453 100644 (file)
@@ -74,11 +74,9 @@ class Ingo_Storage_Rule
             $output = (empty($data)) ? array() : preg_split("/\s+/", $data);
         }
 
-        if ($sort) {
-            $output = Horde_Array::prepareAddressList($output);
-        }
-
-        return $output;
+        return $sort
+            ? Horde_Array::prepareAddressList($output)
+            : $output;
     }
 
 }
index 9f3f2da..dfc61e5 100644 (file)
@@ -18,24 +18,37 @@ class Ingo_Storage_Spam extends Ingo_Storage_Rule
      */
     protected $_obtype = Ingo_Storage::ACTION_SPAM;
 
+    /**
+     */
     protected $_folder = null;
+
+    /**
+     */
     protected $_level = 5;
 
+    /**
+     */
     public function setSpamFolder($folder)
     {
         $this->_folder = $folder;
     }
 
+    /**
+     */
     public function setSpamLevel($level)
     {
         $this->_level = $level;
     }
 
+    /**
+     */
     public function getSpamFolder()
     {
         return $this->_folder;
     }
 
+    /**
+     */
     public function getSpamLevel()
     {
         return $this->_level;
index 07d68bf..584b7c9 100644 (file)
@@ -139,10 +139,7 @@ class Ingo_Storage_Sql extends Ingo_Storage
         case self::ACTION_WHITELIST:
             if ($field == self::ACTION_BLACKLIST) {
                 $ob = new Ingo_Storage_Blacklist();
-                $filters = &$this->retrieve(self::ACTION_FILTERS);
-                if (is_a($filters, 'PEAR_Error')) {
-                    return $filters;
-                }
+                $filters = $this->retrieve(self::ACTION_FILTERS);
                 $rule = $filters->findRule($field);
                 if (isset($rule['action-value'])) {
                     $ob->setBlacklistFolder($rule['action-value']);
@@ -271,10 +268,7 @@ class Ingo_Storage_Sql extends Ingo_Storage
         case self::ACTION_WHITELIST:
             $is_blacklist = (int)($ob->obType() == self::ACTION_BLACKLIST);
             if ($is_blacklist) {
-                $filters = &$this->retrieve(self::ACTION_FILTERS);
-                if (is_a($filters, 'PEAR_Error')) {
-                    return $filters;
-                }
+                $filters = $this->retrieve(self::ACTION_FILTERS);
                 $id = $filters->findRuleId(self::ACTION_BLACKLIST);
                 if ($id !== null) {
                     $rule = $filters->getRule($id);
@@ -402,12 +396,12 @@ class Ingo_Storage_Sql extends Ingo_Storage
      *
      * @param string $user  The user name to delete filters for.
      *
-     * @return mixed  True | PEAR_Error
+     * @throws Ingo_Exception
      */
-    function removeUserData($user)
+    public function removeUserData($user)
     {
         if (!Horde_Auth::isAdmin() && $user != Horde_Auth::getAuth()) {
-            return PEAR::raiseError(_("Permission Denied"));
+            throw new Ingo_Exception(_("Permission Denied"));
         }
 
         $queries = array(sprintf('DELETE FROM %s WHERE rule_owner = ?',
@@ -425,8 +419,8 @@ class Ingo_Storage_Sql extends Ingo_Storage
         foreach ($queries as $query) {
             Horde::logMessage('Ingo_Storage_sql::removeUserData(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
             $result = $this->_write_db->query($query, $values);
-            if (is_a($result, 'PEAR_Error')) {
-                return $result;
+            if ($result instanceof PEAR_Error) {
+                throw new Ingo_Exception($result);
             }
         }
 
index 08bc6e9..43e7672 100644 (file)
  */
 class Ingo_Storage_Vacation extends Ingo_Storage_Rule
 {
+
+    /**
+     */
     protected $_addr = array();
+
+    /**
+     */
     protected $_days = 7;
+
+    /**
+     */
     protected $_excludes = array();
+
+    /**
+     */
     protected $_ignorelist = true;
+
+    /**
+     */
     protected $_reason = '';
+
+    /**
+     */
     protected $_subject = '';
+
+    /**
+     */
     protected $_start;
+
+    /**
+     */
     protected $_end;
+
+    /**
+     */
     protected $_obtype = Ingo_Storage::ACTION_VACATION;
 
+    /**
+     */
     public function setVacationAddresses($data, $sort = true)
     {
         $this->_addr = $this->_addressList($data, $sort);
     }
 
+    /**
+     */
     public function setVacationDays($data)
     {
         $this->_days = $data;
     }
 
+    /**
+     */
     public function setVacationExcludes($data, $sort = true)
     {
         $this->_excludes = $this->_addressList($data, $sort);
     }
 
+    /**
+     */
     public function setVacationIgnorelist($data)
     {
         $this->_ignorelist = $data;
     }
 
+    /**
+     */
     public function setVacationReason($data)
     {
         $this->_reason = $data;
     }
 
+    /**
+     */
     public function setVacationSubject($data)
     {
         $this->_subject = $data;
     }
 
+    /**
+     */
     public function setVacationStart($data)
     {
         $this->_start = $data;
     }
 
+    /**
+     */
     public function setVacationEnd($data)
     {
         $this->_end = $data;
     }
 
+    /**
+     */
     public function getVacationAddresses()
     {
         try {
@@ -70,66 +115,92 @@ class Ingo_Storage_Vacation extends Ingo_Storage_Rule
         }
     }
 
+    /**
+     */
     public function getVacationDays()
     {
         return $this->_days;
     }
 
+    /**
+     */
     public function getVacationExcludes()
     {
         return $this->_excludes;
     }
 
+    /**
+     */
     public function getVacationIgnorelist()
     {
         return $this->_ignorelist;
     }
 
+    /**
+     */
     public function getVacationReason()
     {
         return $this->_reason;
     }
 
+    /**
+     */
     public function getVacationSubject()
     {
         return $this->_subject;
     }
 
+    /**
+     */
     public function getVacationStart()
     {
         return $this->_start;
     }
 
+    /**
+     */
     public function getVacationStartYear()
     {
         return date('Y', $this->_start);
     }
 
+    /**
+     */
     public function getVacationStartMonth()
     {
         return date('n', $this->_start);
     }
 
+    /**
+     */
     public function getVacationStartDay()
     {
         return date('j', $this->_start);
     }
 
+    /**
+     */
     public function getVacationEnd()
     {
         return $this->_end;
     }
 
+    /**
+     */
     public function getVacationEndYear()
     {
         return date('Y', $this->_end);
     }
 
+    /**
+     */
     public function getVacationEndMonth()
     {
         return date('n', $this->_end);
     }
 
+    /**
+     */
     public function getVacationEndDay()
     {
         return date('j', $this->_end);
index 682b5d4..f0fa926 100644 (file)
  */
 class Ingo_Storage_VacationTest extends Ingo_Storage_Vacation
 {
+    /**
+     */
     public function getVacationAddresses()
     {
         return $this->_addr;
     }
+
 }
index 38c5683..919b92f 100644 (file)
  */
 class Ingo_Storage_Whitelist extends Ingo_Storage_Rule
 {
+    /**
+     */
     protected $_addr = array();
+
+    /**
+     */
     protected $_obtype = Ingo_Storage::ACTION_WHITELIST;
 
     /**
@@ -20,7 +25,8 @@ class Ingo_Storage_Whitelist extends Ingo_Storage_Rule
      * @param mixed $data    The list of addresses (array or string).
      * @param boolean $sort  Sort the list?
      *
-     * @return mixed  PEAR_Error on error, true on success.
+     * @return boolean  True on success.
+     * @throws Ingo_Exception
      */
     public function setWhitelist($data, $sort = true)
     {
@@ -29,7 +35,7 @@ class Ingo_Storage_Whitelist extends Ingo_Storage_Rule
         if (!empty($GLOBALS['conf']['storage']['maxwhitelist'])) {
             $addr_count = count($addr);
             if ($addr_count > $GLOBALS['conf']['storage']['maxwhitelist']) {
-                return PEAR::raiseError(sprintf(_("Maximum number of whitelisted addresses exceeded (Total addresses: %s, Maximum addresses: %s).  Could not add new addresses to whitelist."), $addr_count, $GLOBALS['conf']['storage']['maxwhitelist']), 'horde.error');
+                throw new Ingo_Exception(sprintf(_("Maximum number of whitelisted addresses exceeded (Total addresses: %s, Maximum addresses: %s).  Could not add new addresses to whitelist."), $addr_count, $GLOBALS['conf']['storage']['maxwhitelist']), 'horde.error');
             }
         }
 
@@ -37,6 +43,8 @@ class Ingo_Storage_Whitelist extends Ingo_Storage_Rule
         return true;
     }
 
+    /**
+     */
     public function getWhitelist()
     {
         return empty($this->_addr)
index 2c5e472..03b61c8 100644 (file)
@@ -41,12 +41,11 @@ if (empty($availActions)) {
 require INGO_BASE . '/config/fields.php';
 
 /* Get the current rules. */
-$filters = &$ingo_storage->retrieve(Ingo_Storage::ACTION_FILTERS);
+$filters = $ingo_storage->retrieve(Ingo_Storage::ACTION_FILTERS);
 
 /* Run through action handlers. */
-$actionID = Horde_Util::getFormData('actionID');
-$edit_number = Horde_Util::getFormData('edit');
-switch ($actionID) {
+$vars = Horde_Variables::getDefaultVariables();
+switch ($vars->actionID) {
 case 'create_folder':
 case 'rule_save':
 case 'rule_update':
@@ -58,26 +57,24 @@ case 'rule_delete':
     }
 
     $rule = array(
-        'id' => Horde_Util::getFormData('id'),
-        'name' => Horde_Util::getFormData('name'),
-        'combine' => Horde_Util::getFormData('combine'),
+        'id' => $vars->id,
+        'name' => $vars->name,
+        'combine' => $vars->combine,
         'conditions' => array()
     );
 
-    $field = Horde_Util::getFormData('field');
-    $match = Horde_Util::getFormData('match');
-    $userheader = Horde_Util::getFormData('userheader');
-    $value = Horde_Util::getFormData('value');
     if ($ingo_script->caseSensitive()) {
-        $casesensitive = Horde_Util::getFormData('case');
+        $casesensitive = $vars->case;
     }
 
     $valid = true;
-    foreach ($field as $key => $val) {
+    foreach ($vars->field as $key => $val) {
         if (!empty($val)) {
             $condition = array();
             if ($val == Ingo::USER_HEADER) {
-                $condition['field'] = (empty($userheader[$key])) ? '' : $userheader[$key];
+                $condition['field'] = empty($vars->userheader[$key])
+                    ? ''
+                    : $vars->userheader[$key];
                 $condition['type'] = Ingo_Storage::TYPE_HEADER;
             } elseif (!isset($ingo_fields[$val])) {
                 $condition['field'] = $val;
@@ -86,35 +83,38 @@ case 'rule_delete':
                 $condition['field'] = $val;
                 $condition['type'] = $ingo_fields[$val]['type'];
             }
-            $condition['match'] = isset($match[$key]) ? $match[$key] : '';
+            $condition['match'] = isset($vars->match[$key])
+                ? $vars->match[$key]
+                : '';
 
-            if ($actionID == 'rule_save'
-                && empty($value[$key])
-                && $condition['match'] != 'exists'
-                && $condition['match'] != 'not exist') {
+            if (($vars->actionID == 'rule_save') &&
+                empty($vars->value[$key]) &&
+                !in_array($condition['match'], array('exists', 'not exist'))) {
                 $notification->push(sprintf(_("You cannot create empty conditions. Please fill in a value for \"%s\"."), $condition['field']), 'horde.error');
                 $valid = false;
             }
-            $condition['value'] = isset($value[$key]) ? $value[$key] : '';
+            $condition['value'] = isset($vars->value[$key])
+                ? $vars->value[$key]
+                : '';
 
             if (isset($casesensitive)) {
-                $condition['case'] = isset($casesensitive[$key]) ? $casesensitive[$key] : '';
+                $condition['case'] = isset($casesensitive[$key])
+                    ? $casesensitive[$key]
+                    : '';
             }
             $rule['conditions'][] = $condition;
         }
     }
 
-    if ($actionID == 'create_folder') {
-        $rule['action-value'] = Ingo::createFolder(Horde_Util::getFormData('new_folder_name'));
-    } else {
-        $rule['action-value'] = Horde_Util::getFormData('actionvalue');
-    }
+    $rule['action-value'] = ($vars->actionID == 'create_folder')
+        ? Ingo::createFolder($vars->new_folder_name)
+        : $vars->actionvalue;
 
-    $rule['action'] = Horde_Util::getFormData('action');
-    $rule['stop'] = Horde_Util::getFormData('stop');
+    $rule['action'] = $vars->action;
+    $rule['stop'] = $vars->stop;
 
     $rule['flags'] = 0;
-    $flags = Horde_Util::getFormData('flags', array());
+    $flags = $vars->flags || array();
     if (!empty($flags)) {
         foreach ($flags as $val) {
             $rule['flags'] |= $val;
@@ -125,8 +125,8 @@ case 'rule_delete':
     $_SESSION['ingo']['change'] = time();
 
     /* Save the rule. */
-    if ($actionID == 'rule_save' && $valid) {
-        if (is_null($edit_number)) {
+    if ($vars->actionID == 'rule_save' && $valid) {
+        if (!isset($vars->edit)) {
             if ($GLOBALS['perms']->hasAppPermission('max_rules') !== true &&
                 $GLOBALS['perms']->hasAppPermission('max_rules') <= count($filters->getFilterList())) {
                 header('Location: ' . Horde::applicationUrl('filters.php', true));
@@ -134,7 +134,7 @@ case 'rule_delete':
             }
             $filters->addRule($rule);
         } else {
-            $filters->updateRule($rule, $edit_number);
+            $filters->updateRule($rule, $vars->edit);
         }
         $ingo_storage->store($filters);
         $notification->push(_("Changes saved."), 'horde.success');
@@ -145,15 +145,16 @@ case 'rule_delete':
 
         header('Location: ' . Horde::applicationUrl('filters.php'));
         exit;
-    } elseif ($actionID == 'rule_delete') {
+    }
+
+    if ($vars->actionID == 'rule_delete') {
         if (!Ingo::hasSharePermission(Horde_Perms::DELETE)) {
             $notification->push(_("You do not have permission to delete filter rules."), 'horde.error');
             header('Location: ' . Horde::applicationUrl('filters.php', true));
             exit;
         }
-        $cond_num = Horde_Util::getFormData('conditionnumber');
-        if (!is_null($cond_num)) {
-            unset($rule['conditions'][$cond_num]);
+        if (isset($vars->conditionnumber)) {
+            unset($rule['conditions'][$vars->conditionnumner]);
             $rule['conditions'] = array_values($rule['conditions']);
         }
     }
@@ -165,7 +166,7 @@ default:
         header('Location: ' . Horde::applicationUrl('filters.php', true));
         exit;
     }
-    if (is_null($edit_number)) {
+    if (!isset($vars->edit)) {
         if ($GLOBALS['perms']->hasAppPermission('max_rules') !== true &&
             $GLOBALS['perms']->hasAppPermission('max_rules') <= count($filters->getFilterList())) {
             try {
@@ -179,7 +180,7 @@ default:
         }
         $rule = $filters->getDefaultRule();
     } else {
-        $rule = $filters->getRule($edit_number);
+        $rule = $filters->getRule($vars->edit);
     }
     break;
 }
@@ -254,11 +255,10 @@ foreach ($rule['conditions'] as $cond_num => $condition) {
             '<option value="' . Ingo::USER_HEADER . '"' . ((!$option_selected) ? ' selected="selected"' : '') . '>' . _("Self-Defined Header") . (($lastfield) ? '' : ':') . "</option>\n";
         if (!$option_selected) {
             $header_entry = true;
-            $userheader = Horde_Util::getFormData('userheader');
-            if (empty($userheader)) {
-                $userheader = isset($condition['field']) ? $condition['field'] : '';
+            if (empty($vars->userheader)) {
+                $vars->userheader = isset($condition['field']) ? $condition['field'] : '';
             } else {
-                $userheader = $userheader[$cond_num];
+                $vars->userheader = $vars->userheader[$cond_num];
             }
         }
     }
index 70199b4..e865eeb 100644 (file)
@@ -21,10 +21,9 @@ $script = '';
 
 /* Get the Ingo_Script:: backend. */
 $scriptor = Ingo::loadIngoScript();
-if ($scriptor) {
-    /* Generate the script. */
-    $script = $scriptor->generate();
-}
+
+/* Generate the script. */
+$script = $scriptor->generate();
 
 /* Activate/deactivate script if requested.
    activateScript() does its own $notification->push() on error. */
@@ -41,9 +40,10 @@ case 'action_deactivate':
     break;
 
 case 'show_active':
-    $script = Ingo::getScript();
-    if (is_a($script, 'PEAR_Error')) {
-        $notification->push($script, 'horde.error');
+    try {
+        $script = Ingo::getScript();
+    } catch (Ingo_Exception $e) {
+        $notification->push($e);
         $script = '';
     }
     break;
index 5953147..14c3df2 100755 (executable)
@@ -134,19 +134,20 @@ function smtpd_access_policy($query)
         // Retrieve the data.
         $GLOBALS['auth']->setAuth($user, array());
         $_SESSION['ingo']['current_share'] = ':' . $user;
-        $wl = $GLOBALS['rules_storage']->retrieve(Ingo_Storage::ACTION_WHITELIST, false);
-        $bl = $GLOBALS['rules_storage']->retrieve(Ingo_Storage::ACTION_BLACKLIST, false);
 
-        // Fill in data from saved rules.
-        if (!is_a($wl, 'PEAR_Error')) {
-            $whitelists[$user] = $wl->getWhitelist();
-        }
-        if (!is_a($bl, 'PEAR_Error') && !$bl->getBlacklistFolder()) {
-            // We will only reject email at delivery time if the user
-            // wants blacklisted mail deleted completely, not filed
-            // into a separate folder.
-            $blacklists[$user] = $bl->getBlacklist();
-        }
+        try {
+            $whitelists[$user] = $GLOBALS['rules_storage']->retrieve(Ingo_Storage::ACTION_WHITELIST, false)->getWhitelist();
+        } catch (Ingo_Exception $e) {}
+
+        try {
+            $bl = $GLOBALS['rules_storage']->retrieve(Ingo_Storage::ACTION_BLACKLIST, false);
+            if (!$bl->getBlacklistFolder()) {
+                // We will only reject email at delivery time if the user
+                // wants blacklisted mail deleted completely, not filed
+                // into a separate folder.
+                $blacklists[$user] = $bl->getBlacklist();
+            }
+        } catch (Ingo_Exception $e) {}
     }
 
     // Check whitelist rules first so that mistaken overlap doesn't
index 01f28a8..84f7d33 100755 (executable)
@@ -57,7 +57,7 @@ while (!feof(STDIN)) {
     foreach ($rules as $rule) {
         $filter = $prefs_storage->retrieve($rule, false);
         if ($rule == Ingo_Storage::ACTION_FILTERS) {
-            $new_filter = &$sql_storage->retrieve(Ingo_Storage::ACTION_FILTERS, true, true);
+            $new_filter = $sql_storage->retrieve(Ingo_Storage::ACTION_FILTERS, true, true);
             foreach ($filter->getFilterList() as $rule) {
                 $new_filter->addRule($rule);
                 echo '.';
index 2cb9ffa..f06099e 100644 (file)
@@ -48,13 +48,13 @@ if (!in_array(Ingo_Storage::ACTION_SPAM, $_SESSION['ingo']['script_categories'])
 }
 
 /* Get the spam object and rule. */
-$spam = &$ingo_storage->retrieve(Ingo_Storage::ACTION_SPAM);
-$filters = &$ingo_storage->retrieve(Ingo_Storage::ACTION_FILTERS);
+$spam = $ingo_storage->retrieve(Ingo_Storage::ACTION_SPAM);
+$filters = $ingo_storage->retrieve(Ingo_Storage::ACTION_FILTERS);
 $spam_id = $filters->findRuleId(Ingo_Storage::ACTION_SPAM);
 $spam_rule = $filters->getRule($spam_id);
 
-$vars = &Horde_Variables::getDefaultVariables();
-if ($vars->get('submitbutton') == _("Return to Rules List")) {
+$vars = Horde_Variables::getDefaultVariables();
+if ($vars->submitbutton == _("Return to Rules List")) {
     header('Location: ' . Horde::applicationUrl('filters.php', true));
     exit;
 }
@@ -63,10 +63,10 @@ if ($vars->get('submitbutton') == _("Return to Rules List")) {
 $form = new Horde_Form($vars);
 $renderer = new Horde_Form_Renderer(array('varrenderer_driver' => array('ingo', 'ingo'), 'encode_title' => false));
 
-$v = &$form->addVariable(_("Spam Level:"), 'level', 'int', false, false, _("Messages with a likely spam score greater than or equal to this number will be treated as spam."));
+$v = $form->addVariable(_("Spam Level:"), 'level', 'int', false, false, _("Messages with a likely spam score greater than or equal to this number will be treated as spam."));
 $v->setHelp('spam-level');
 
-$folder_var = &$form->addVariable(_("Folder to receive spam:"), 'folder', 'ingo_folders', false);
+$folder_var = $form->addVariable(_("Folder to receive spam:"), 'folder', 'ingo_folders', false);
 $folder_var->setHelp('spam-folder');
 $form->addHidden('', 'actionID', 'text', false);
 $form->addHidden('', 'new_folder_name', 'text', false);
@@ -78,9 +78,9 @@ if ($form->validate($vars)) {
     $success = true;
 
     // Create a new folder if requested.
-    if ($vars->get('actionID') == 'create_folder') {
+    if ($vars->actionID == 'create_folder') {
         try {
-            $result = Ingo::createFolder($vars->get('new_folder_name'));
+            $result = Ingo::createFolder($vars->new_folder_name);
             if ($result) {
                 $spam->setSpamFolder($result);
             } else {
@@ -88,39 +88,33 @@ if ($form->validate($vars)) {
             }
         } catch (Horde_Exception $e) {
             $success = false;
-            $notification->push($e->getMessage());
+            $notification->push($e);
         }
     } else {
-        $spam->setSpamFolder($vars->get('folder'));
+        $spam->setSpamFolder($vars->folder);
     }
 
-    $spam->setSpamLevel($vars->get('level'));
+    $spam->setSpamLevel($vars->level);
 
-    if (is_a($result = $ingo_storage->store($spam), 'PEAR_Error')) {
-        $notification->push($result);
-        $success = false;
-    } else {
+    try {
+        $ingo_storage->store($spam):
         $notification->push(_("Changes saved."), 'horde.success');
-        if ($vars->get('submitbutton') == _("Save and Enable")) {
+        if ($vars->submitbutton == _("Save and Enable")) {
             $filters->ruleEnable($spam_id);
-            if (is_a($result = $ingo_storage->store($filters), 'PEAR_Error')) {
-                $notification->push($result);
-                $success = false;
-            } else {
-                $notification->push(_("Rule Enabled"), 'horde.success');
-                $spam_rule['disable'] = false;
-            }
-        } elseif ($vars->get('submitbutton') == _("Save and Disable")) {
+            $ingo_storage->store($filters);
+            $notification->push(_("Rule Enabled"), 'horde.success');
+            $spam_rule['disable'] = false;
+        } elseif ($vars->submitbutton == _("Save and Disable")) {
             $filters->ruleDisable($spam_id);
-            if (is_a($result = $ingo_storage->store($filters), 'PEAR_Error')) {
-                $notification->push($result);
-                $success = false;
-            } else {
-                $notification->push(_("Rule Disabled"), 'horde.success');
-                $spam_rule['disable'] = true;
-            }
+            $ingo_storage->store($filters);
+            $notification->push(_("Rule Disabled"), 'horde.success');
+            $spam_rule['disable'] = true;
         }
+    } catch (Ingo_Exception $e) {
+        $notification->push($result);
+        $success = false;
     }
+
     if ($success && $prefs->getValue('auto_update')) {
         Ingo::updateScript();
     }
@@ -140,10 +134,10 @@ $form->appendButtons(_("Return to Rules List"));
 /* Set default values. */
 $folder_var->type->setFolder($spam->getSpamFolder());
 if (!$form->isSubmitted()) {
-    $vars->set('level', $spam->getSpamLevel());
-    $vars->set('folder', $spam->getSpamFolder());
-    $vars->set('actionID', '');
-    $vars->set('new_folder_name', '');
+    $vars->level = $spam->getSpamLevel();
+    $vars->folder = $spam->getSpamFolder();
+    $vars->actionID = '';
+    $vars->new_folder_name = '';
 }
 
 /* Set form title. */
index d4ef8bd..77238b1 100644 (file)
@@ -13,7 +13,7 @@
    </select>
 <?php if ($header_entry): ?>
    <label for="userheader_<?php echo (int)$cond_num ?>" class="hidden"><?php echo _("User header") ?></label>
-   <input id="userheader_<?php echo (int)$cond_num ?>" name="userheader[<?php echo (int)$cond_num ?>]" value="<?php echo htmlspecialchars($userheader) ?>" />
+   <input id="userheader_<?php echo (int)$cond_num ?>" name="userheader[<?php echo (int)$cond_num ?>]" value="<?php echo htmlspecialchars($vars->userheader) ?>" />
 <?php endif; ?>
   </td>
 <?php if ($lastfield): ?>
index 24ce582..88f11e3 100644 (file)
@@ -3,8 +3,8 @@
 <input type="hidden" name="actionID" value="rule_update" />
 <input type="hidden" name="conditionnumber" value="-1" />
 <input type="hidden" name="new_folder_name" value="" />
-<?php if (!is_null($edit_number)): ?>
-<input type="hidden" name="edit" value="<?php echo $edit_number ?>" />
+<?php if (isset($vars->edit)): ?>
+<input type="hidden" name="edit" value="<?php echo $vars->edit ?>" />
 <?php endif; if (isset($rule['id'])): ?>
 <input type="hidden" name="id" value="<?php echo $rule['id'] ?>" />
 <?php endif; ?>
index ed248ed..6ffafda 100644 (file)
@@ -21,14 +21,14 @@ if (!in_array(Ingo_Storage::ACTION_VACATION, $_SESSION['ingo']['script_categorie
 }
 
 /* Get vacation object and rules. */
-$vacation = &$ingo_storage->retrieve(Ingo_Storage::ACTION_VACATION);
-$filters = &$ingo_storage->retrieve(Ingo_Storage::ACTION_FILTERS);
+$vacation = $ingo_storage->retrieve(Ingo_Storage::ACTION_VACATION);
+$filters = $ingo_storage->retrieve(Ingo_Storage::ACTION_FILTERS);
 $vac_id = $filters->findRuleId(Ingo_Storage::ACTION_VACATION);
 $vac_rule = $filters->getRule($vac_id);
 
 /* Load libraries. */
 $vars = Horde_Variables::getDefaultVariables();
-if ($vars->get('submitbutton') == _("Return to Rules List")) {
+if ($vars->submitbutton == _("Return to Rules List")) {
     header('Location: ' . Horde::applicationUrl('filters.php', true));
     exit;
 }
@@ -37,21 +37,21 @@ if ($vars->get('submitbutton') == _("Return to Rules List")) {
 $form = new Horde_Form($vars);
 $form->setSection('basic', _("Basic Settings"));
 
-$v = &$form->addVariable(_("Start of vacation:"), 'start', 'monthdayyear', '');
+$v = $form->addVariable(_("Start of vacation:"), 'start', 'monthdayyear', '');
 $v->setHelp('vacation-period');
 $form->addVariable(_("End of vacation:"), 'end', 'monthdayyear', '');
-$v = &$form->addVariable(_("Subject of vacation message:"), 'subject', 'text', false);
+$v = $form->addVariable(_("Subject of vacation message:"), 'subject', 'text', false);
 $v->setHelp('vacation-subject');
-$v = &$form->addVariable(_("Reason:"), 'reason', 'longtext', false, false, null, array(10, 40));
+$v = $form->addVariable(_("Reason:"), 'reason', 'longtext', false, false, null, array(10, 40));
 $v->setHelp('vacation-reason');
 $form->setSection('advanced', _("Advanced Settings"));
-$v = &$form->addVariable(_("My email addresses:"), 'addresses', 'longtext', true, false, null, array(5, 40));
+$v = $form->addVariable(_("My email addresses:"), 'addresses', 'longtext', true, false, null, array(5, 40));
 $v->setHelp('vacation-myemail');
-$v = &$form->addVariable(_("Addresses to not send responses to:"), 'excludes', 'longtext', false, false, null, array(10, 40));
+$v = $form->addVariable(_("Addresses to not send responses to:"), 'excludes', 'longtext', false, false, null, array(10, 40));
 $v->setHelp('vacation-noresponse');
-$v = &$form->addVariable(_("Do not send responses to bulk or list messages?"), 'ignorelist', 'boolean', false);
+$v = $form->addVariable(_("Do not send responses to bulk or list messages?"), 'ignorelist', 'boolean', false);
 $v->setHelp('vacation-bulk');
-$v = &$form->addVariable(_("Number of days between vacation replies:"), 'days', 'int', false);
+$v = $form->addVariable(_("Number of days between vacation replies:"), 'days', 'int', false);
 $v->setHelp('vacation-days');
 $form->setButtons(_("Save"));
 
@@ -68,30 +68,23 @@ if ($form->validate($vars)) {
     $vacation->setVacationEnd($info['end']);
 
     $success = true;
-    if (is_a($result = $ingo_storage->store($vacation), 'PEAR_Error')) {
-        $notification->push($result);
-        $success = false;
-    } else {
+    try {
+        $ingo_storage->store($vacation);
         $notification->push(_("Changes saved."), 'horde.success');
-        if ($vars->get('submitbutton') == _("Save and Enable")) {
+        if ($vars->submitbutton == _("Save and Enable")) {
             $filters->ruleEnable($vac_id);
-            if (is_a($result = $ingo_storage->store($filters), 'PEAR_Error')) {
-                $notification->push($result);
-                $success = false;
-            } else {
-                $notification->push(_("Rule Enabled"), 'horde.success');
-                $vac_rule['disable'] = false;
-            }
+            $ingo_storage->store($filters);
+            $notification->push(_("Rule Enabled"), 'horde.success');
+            $vac_rule['disable'] = false;
         } elseif ($vars->get('submitbutton') == _("Save and Disable")) {
             $filters->ruleDisable($vac_id);
-            if (is_a($result = $ingo_storage->store($filters), 'PEAR_Error')) {
-                $notification->push($result);
-                $success = false;
-            } else {
-                $notification->push(_("Rule Disabled"), 'horde.success');
-                $vac_rule['disable'] = true;
-            }
+            $ingo_storage->store($filters);
+            $notification->push(_("Rule Disabled"), 'horde.success');
+            $vac_rule['disable'] = true;
         }
+    } catch (Ingo_Exception $e) {
+        $notification->push($result);
+        $success = false;
     }
 
     if ($success && $prefs->getValue('auto_update')) {
index 17d0940..8169f3e 100644 (file)
@@ -22,16 +22,13 @@ if (!in_array(Ingo_Storage::ACTION_WHITELIST, $_SESSION['ingo']['script_categori
     exit;
 }
 
-$whitelist = &$ingo_storage->retrieve(Ingo_Storage::ACTION_WHITELIST);
+$whitelist = $ingo_storage->retrieve(Ingo_Storage::ACTION_WHITELIST);
 
 /* Perform requested actions. */
-$actionID = Horde_Util::getFormData('actionID');
-switch ($actionID) {
+switch (Horde_Util::getFormData('actionID')) {
 case 'rule_update':
-    $ret = $whitelist->setWhitelist(Horde_Util::getFormData('whitelist'));
-    if (is_a($ret, 'PEAR_Error')) {
-        $notification->push($ret, $ret->getCode());
-    } else {
+    try {
+        $whitelist->setWhitelist(Horde_Util::getFormData('whitelist'));
         if (!$ingo_storage->store($whitelist)) {
             $notification->push("Error saving changes.", 'horde.error');
         } else {
@@ -45,13 +42,14 @@ case 'rule_update':
 
         /* Update the timestamp for the rules. */
         $_SESSION['ingo']['change'] = time();
+    } catch (Ingo_Exception $e) {
+        $notification->push($e);
     }
-
     break;
 }
 
 /* Get the whitelist rule. */
-$filters = &$ingo_storage->retrieve(Ingo_Storage::ACTION_FILTERS);
+$filters = $ingo_storage->retrieve(Ingo_Storage::ACTION_FILTERS);
 $wl_rule = $filters->findRule(Ingo_Storage::ACTION_WHITELIST);
 
 $title = _("Whitelist Edit");