[SOLVED] Exim + mysql + postfixadmin + autoreply + utf8

EXIM, sendmail, postfix, Dovecot и прочие. Решение проблем связанных с работой электронной почты

Модератор: xM

Правила форума
Убедительная просьба юзать теги [code] при оформлении листингов.
Сообщения не оформленные должным образом имеют все шансы быть незамеченными.
Hety
рядовой
Сообщения: 13
Зарегистрирован: 2009-07-01 10:50:18

[SOLVED] Exim + mysql + postfixadmin + autoreply + utf8

Непрочитанное сообщение Hety » 2009-12-15 22:29:26

Помучавшись изрядно и просмотрев полкило доков наконец победил автореплай + мускуль. Решение может пойти не для всех из-за специфичности решения в части мускуля:

1. MySQL
Я выставил utf8 дефолтом для всего: соединения, клиент, резалт итп. Не помогло. Вариантов овказалось два(три, но третий я не пробовал осуществить ввиду отсутствия необходимости). Или сделать

Код: Выделить всё

init_connect="SET NAMES utf8"
(при соединении каждый клиент отдает эту строку серверу. root - исключение)
или

Код: Выделить всё

skip-character-set-client-handshake
(при соединении игнорирует запрос клиентом определенного чарсета. но при желании клиент может сделать SET NAMES уже после соединения)
Я использовал скип. Вариант показался мне более православным.

2. Exim

Код: Выделить всё

userautoreply:
   driver = accept
   domains = ${lookup mysql{SELECT domain FROM vacation WHERE \
                       domain='${quote_mysql:$domain}' AND \
                       email='${quote_mysql:$local_part@$domain}' AND \
                       active='1'}{$value}}
    transport = userautoreply
   unseen
ДО основного доставщика почты. unseen гарантирует, что письмо пойдет дальше по роутерам. Это тут разжевано, заостряться не будем.

Код: Выделить всё

userautoreply:
        driver = autoreply
        headers = "Content-Type: text/plain; charset=utf-8\nContent-Transfer-Encoding: 7bit"
        user = mailnull
        group = mail
        to = "${sender_address}"
        from = "${local_part}@${domain}"
        subject = ${lookup mysql {SELECT subject FROM vacation \
                WHERE domain='${quote_mysql:$domain}' AND \
                email='${quote_mysql:$local_part@$domain}'}{$value}}
        text = ${lookup mysql {SELECT body FROM vacation \
                WHERE domain='${quote_mysql:$domain}' AND \
                email='${quote_mysql:$local_part@$domain}'}{$value}}
Автореплай призван доставлять. Доставлять автореплаи. Берем сабж, берем текст. И доставляем. Обработанный напильником мускуль позволяет брать utf8 как есть, без превращения в белиберду.

Код: Выделить всё

Content-Transfer-Encoding: 7bit
- это, возможно, не обязательно. Без - не проверял. Работает и хорошо. Еще одна фигня в том, что в сабже должен быть ascii, иначе экзим ругается (справедливо) на некошерные символы. Но эту траблу мы будем решать в постфиксадмине. Его пилить проще.

3. Postfixadmin
Вот тут напильник очень кстати. Трабл несколько. Во-первых, в алиасы запихивается дополнительный адрес аутореплай домена. Нам он там не вперся. Во-вторых, нам нужен закодированный сабж. В пыхе я не силен (не силен = сегодня второй раз в жизни пилил его напильником). Наверняка есть масса более православных способов поправить edit-vacation.php, но, как говорится, работает - трогать не буду.
Алиасы: ищем строки и комментим их, в алиасы будет добавляться запятая. Ну и черт с ней. Экзим она не парит.

Код: Выделить всё

#$vacation_goto = preg_replace('/@/', '#', $fUsername);
#$vacation_goto = $vacation_goto . '@' . $vacation_domain;
Далее будем бороться с энкодингом.

Код: Выделить всё

      if($result['rows'] == 1) {
          $fSubject = "=?UTF-8?B?".base64_encode($fSubject)."?=";
          $result = db_query("UPDATE $table_vacation SET active = '$Active', subject = '$fSubject', body = '$fBody', created = NOW() WHERE email = '$fUsername'");
      }
      else {
          $fSubject = "=?UTF-8?B?".base64_encode($fSubject)."?=";
          $result = db_query ("INSERT INTO $table_vacation (email,subject,body,domain,created,active) VALUES ('$fUsername','$fSubject','$fBody','$fDomain',NOW(),'$Active')");
      }
Ищем запросы, которые непосредственно добавляют данные в базу и предварительно энкодим сабж.

Собственно всё.

Третий путь, о котором я говорил ранее - заставить экзим говорить SET NAMES utf8 перед запросами сабжа и текста. Это был бы самый верный способ победить засаду с мускулем. Но мои ручки оказались кривоваты для этого

Хостинговая компания Host-Food.ru
Хостинг HostFood.ru
 

Услуги хостинговой компании Host-Food.ru

Хостинг HostFood.ru

Тарифы на хостинг в России, от 12 рублей: https://www.host-food.ru/tariffs/hosting/
Тарифы на виртуальные сервера (VPS/VDS/KVM) в РФ, от 189 руб.: https://www.host-food.ru/tariffs/virtualny-server-vps/
Выделенные сервера, Россия, Москва, от 2000 рублей (HP Proliant G5, Intel Xeon E5430 (2.66GHz, Quad-Core, 12Mb), 8Gb RAM, 2x300Gb SAS HDD, P400i, 512Mb, BBU):
https://www.host-food.ru/tariffs/vydelennyi-server-ds/
Недорогие домены в популярных зонах: https://www.host-food.ru/domains/

Аватара пользователя
Alex Keda
стреляли...
Сообщения: 35352
Зарегистрирован: 2004-10-18 14:25:19
Откуда: Made in USSR
Контактная информация:

Re: [SOLVED] Exim + mysql + postfixadmin + autoreply + utf8

Непрочитанное сообщение Alex Keda » 2010-02-28 14:21:48

файл, конечно, было бы интересней...
Убей их всех! Бог потом рассортирует...


Аватара пользователя
Alex Keda
стреляли...
Сообщения: 35352
Зарегистрирован: 2004-10-18 14:25:19
Откуда: Made in USSR
Контактная информация:

Re: [SOLVED] Exim + mysql + postfixadmin + autoreply + utf8

Непрочитанное сообщение Alex Keda » 2010-02-28 19:41:16

текст из файла брать и прочее
Убей их всех! Бог потом рассортирует...

Hety
рядовой
Сообщения: 13
Зарегистрирован: 2009-07-01 10:50:18

Re: [SOLVED] Exim + mysql + postfixadmin + autoreply + utf8

Непрочитанное сообщение Hety » 2010-02-28 23:47:18

А редактировать это как?

Upd: прилагается патч для постфиксадмина 2.3 с добавлением возможности править самостоятельно автореплай + настройки спамасассина. Включается и отключается доп. функциональность в конфиге.

Upd2: файл не аттачится, ибо запрещено. Патчить gpatch-ем.

Код: Выделить всё

diff -uprN -x config.inc.php postfixadmin/edit-spamassassin.php postfixadmin/edit-spamassassin.php
--- postfixadmin/edit-spamassassin.php	1970-01-01 03:00:00.000000000 +0300
+++ postfixadmin/edit-spamassassin.php	2010-02-15 16:54:31.439389466 +0300
@@ -0,0 +1,181 @@
+<?php
+/*
+ * Author: Nosov Alexander (trashgenerator@gmail.com)
+ *
+ * 
+ * File: edit-spamassassin.php 
+ * Responsible for allowing users to edit their spamassassin settings
+ * Template File: edit-spamassassin.php
+ */
+
+require_once('common.php');
+
+//TODO: maybe move this to general settings.  prefix is not added here now
+$table_spamassassin='userpref';
+
+if($CONF['spamassassin'] != 'YES') { 
+   header("Location: " . $CONF['postfix_admin_url'] . "/list-virtual.php");
+   exit(0);
+}
+
+//user loged in?
+$SESSID_USERNAME = authentication_get_username();
+authentication_require_role('admin');
+
+//user is admin?
+if(authentication_has_role('admin')) {
+   //GET['domain'] or GET['username'] exists? 
+   if (isset($_GET['username'])) {
+      $fUsername = htmlspecialchars(escape_string ($_GET['username']));
+      $tmp = preg_split ('/@/', $fUsername);
+      $fDomain = $tmp[1];
+      //requested username exists?
+      $result = db_query ("SELECT username FROM $table_mailbox WHERE username='$fUsername'");
+      if ($result['rows'] < 1)
+      {
+         header("Location: " . $CONF['postfix_admin_url'] . "/list-virtual.php");
+         die();
+      }
+   } elseif(isset($_GET['domain'])) {
+      $fDomain = htmlspecialchars(escape_string ($_GET['domain']));
+      $fUsername='';
+      //requested domain exists?
+      $result = db_query ("SELECT domain FROM $table_domain WHERE domain='$fDomain'");
+      if ($result['rows'] < 1)
+      {
+         header("Location: " . $CONF['postfix_admin_url'] . "/list-virtual.php");
+         die();
+      }
+   } else {
+      header("Location: " . $CONF['postfix_admin_url'] . "/list-virtual.php");
+      die();
+   }
+   
+   //user have access to selected domain?   
+   if (!(check_owner ($SESSID_USERNAME, $fDomain) || authentication_has_role('global-admin')) )
+   {
+      header("Location: " . $CONF['postfix_admin_url'] . "/list-virtual.php");
+      die();
+   }
+} else {
+   header("Location: " . $CONF['postfix_admin_url'] . "/list-virtual.php");
+   die();
+}
+$fDomain='%'.$fDomain;
+
+
+if ($_SERVER['REQUEST_METHOD'] == "POST") {
+   $currentusername=($fUsername!='')?$fUsername:$fDomain;
+   //base settings
+   if(isset($_POST['spamassassin_submit'])) {
+      //frequired_hits
+      $required_hits=htmlspecialchars(escape_string(safepost('required_hits')));
+      if(safepost('required_hits')==0) {
+         db_query("DELETE FROM $table_spamassassin WHERE username = '$currentusername' AND preference = 'required_hits'");
+      } elseif ($required_hits>0 && $required_hits<=10) {
+         $result = db_query("SELECT * FROM $table_spamassassin WHERE username = '$currentusername' AND preference = 'required_hits'");
+         if($result['rows'] == 1) {
+            db_query("UPDATE $table_spamassassin SET value = '$required_hits' WHERE username = '$currentusername' AND preference = 'required_hits'");
+         }
+         else {
+            db_query ("INSERT INTO $table_spamassassin (username,preference,value) VALUES ('$currentusername','required_hits','$required_hits')");
+         }
+      }
+
+      //subject_tag 
+      $subject_tag=htmlspecialchars(escape_string(safepost('subject_tag')));
+      $result = db_query("SELECT * FROM $table_spamassassin WHERE username = '$currentusername' AND preference = 'subject_tag'");
+      if($result['rows'] == 1) {
+         db_query("UPDATE $table_spamassassin SET value = '$subject_tag' WHERE username = '$currentusername' AND preference = 'subject_tag'");
+      }
+      else {
+         db_query ("INSERT INTO $table_spamassassin (username,preference,value) VALUES ('$currentusername','subject_tag','$subject_tag')");
+      }
+      
+      //whitelist_from
+      $whitelist_from=htmlspecialchars(escape_string(safepost('whitelist_from')));
+      $whitelist_from=str_replace(array('\r\n','\n','\r'),";",$whitelist_from);
+      $result = db_query("SELECT * FROM $table_spamassassin WHERE username = '$currentusername' AND preference = 'whitelist_from'");
+      if($result['rows'] == 1) {
+         db_query("UPDATE $table_spamassassin SET value = '$whitelist_from' WHERE username = '$currentusername' AND preference = 'whitelist_from'");
+      }
+      else {
+         db_query ("INSERT INTO $table_spamassassin (username,preference,value) VALUES ('$currentusername','whitelist_from','$whitelist_from')");
+      }
+      //use_auto_whitelist
+      $use_auto_whitelist=htmlspecialchars(escape_string(safepost('use_auto_whitelist')));
+      $use_auto_whitelist=($use_auto_whitelist=='on') ? 1:0;
+      $result = db_query("SELECT * FROM $table_spamassassin WHERE username = '$currentusername' AND preference = 'use_auto_whitelist'");
+      if($result['rows'] == 1) {
+         db_query("UPDATE $table_spamassassin SET value = '$use_auto_whitelist' WHERE username = '$currentusername' AND preference = 'use_auto_whitelist'");
+      }
+      else {
+         db_query ("INSERT INTO $table_spamassassin (username,preference,value) VALUES ('$currentusername','use_auto_whitelist','$use_auto_whitelist')");
+      }
+
+   }
+   //other settings
+   elseif(isset($_POST['spamassassin_submit_other'])) {
+      //deleting preferences
+      if(count(safepost('fDeletePreferences',array()))>0) {
+         $tDeletePreferencesStr='';
+         foreach(safepost('fDeletePreferences') as $k=>$v) {
+           $tDeletePreferencesStr.=htmlspecialchars(escape_string($k)).', '; 
+         }
+         $tDeletePreferencesStr = substr($tDeletePreferencesStr,0,-2);
+         db_query("DELETE FROM $table_spamassassin WHERE username = '$currentusername' AND prefid IN ($tDeletePreferencesStr)");
+      }
+      //saving custom preferences
+      if(safepost('fPreference')!='') {
+         $tPreference   = htmlspecialchars(escape_string(safepost('fPreference')));
+         $tValue = htmlspecialchars(escape_string(safepost('fValue')));
+         $result = db_query("SELECT * FROM $table_spamassassin WHERE username = '$currentusername' AND preference = '$tPreference'");
+         if($result['rows'] == 1) {
+            db_query("UPDATE $table_spamassassin SET value = '$tValue' WHERE username = '$currentusername' AND preference = '$tPreference'");
+         }
+         else {
+            db_query ("INSERT INTO $table_spamassassin (username,preference,value) VALUES ('$currentusername','$tPreference','$tValue')");
+         }
+      }   
+   }
+   $tMessage='Changes are saved. Probably.';
+}
+
+
+
+
+
+
+$userprefs = array();
+$userprefs_blocked=array();
+//User level or domain
+$sqlwhere=($fUsername!='' && $fDomain!='') ? "username='$fUsername' OR " : '';
+
+$result = db_query ("SELECT * FROM $table_spamassassin WHERE $sqlwhere username='$fDomain' OR username='GLOBAL' ORDER BY username, preference, prefid");
+
+while ($row = db_array ($result['result'])) {
+   $userprefs_blocked[$row['username']][$row['preference']] = $row;
+   $userprefs[$row['preference']]=array();
+}
+foreach($userprefs as $preference=>$value){
+   if(isset($userprefs_blocked[$fUsername][$preference])) {
+      $userprefs[$preference]=$userprefs_blocked[$fUsername][$preference];  
+   } elseif (isset($userprefs_blocked[$fDomain][$preference])) {
+      $userprefs[$preference]=$userprefs_blocked[$fDomain][$preference];  
+   } elseif (isset($userprefs_blocked['GLOBAL'][$preference])) {
+      $userprefs[$preference]=$userprefs_blocked['GLOBAL'][$preference];  
+   } 
+}
+
+if($userprefs['required_hits']) $required_hits=$userprefs['required_hits']['value'];
+if($userprefs['subject_tag']) $subject_tag=$userprefs['subject_tag']['value'];
+if($userprefs['whitelist_from']) $whitelist_from=str_replace(";","\r\n",$userprefs['whitelist_from']['value']);
+if($userprefs['use_auto_whitelist']) $use_auto_whitelist=$userprefs['use_auto_whitelist']['value'];
+//print_r($userprefs_blocked);
+//print_r($userprefs);
+include ("templates/header.php");
+include ("templates/menu.php");
+include ("templates/edit-spamassassin.php");
+include ("templates/footer.php");
+/* vim: set expandtab softtabstop=3 tabstop=3 shiftwidth=3: */
+?>
diff -uprN -x config.inc.php postfixadmin/edit-vacation.php postfixadmin/edit-vacation.php
--- postfixadmin/edit-vacation.php	2009-08-21 16:02:55.000000000 +0400
+++ postfixadmin/edit-vacation.php	2010-01-27 02:09:31.844937000 +0300
@@ -70,7 +70,13 @@ if ($_SERVER['REQUEST_METHOD'] == "GET")
    {
       $row = db_array($result['result']);
       $tMessage = '';
-      $tSubject = $row['subject'];
+
+      if($CONF['vacation_base64']=='YES') {
+         $tSubject = base64_decode(substr($row['subject'],10,-2));
+      } else {
+         $tSubject = $row['subject'];
+      }
+
       $tBody = $row['body'];
    }
 
@@ -85,7 +91,11 @@ if ($_SERVER['REQUEST_METHOD'] == "POST"
 {
 
    $tSubject   = safepost('fSubject');
-   $fSubject   = escape_string ($tSubject);
+   if($CONF['vacation_base64']=='YES') {      
+      $fSubject   = "=?UTF-8?B?".base64_encode(escape_string ($tSubject))."?=";
+   } else {
+      $fSubject   = escape_string ($tSubject);
+   }   
    $tBody      = safepost('fBody');
    $fBody      = escape_string ($tBody);
    $fChange    = escape_string (safepost('fChange'));
@@ -119,26 +129,28 @@ if ($_SERVER['REQUEST_METHOD'] == "POST"
          // retain vacation message if possible - i.e disable vacation away-ness.
          $result = db_query ("UPDATE $table_vacation SET active = '$db_false' WHERE email='$fUsername'");
          $result = db_query("DELETE FROM $table_vacation_notification WHERE on_vacation='$fUsername'");
-
-         $result = db_query ("SELECT * FROM $table_alias WHERE address='$fUsername'");
-         if ($result['rows'] == 1)
+         if(!isset($CONF['no_vacation_alias']) || $CONF['no_vacation_alias']!='YES') 
          {
-            $row = db_array ($result['result']);
-            $goto = $row['goto'];
-            //only one of these will do something, first handles address at beginning and middle, second at end
-            $goto= preg_replace ( "/$vacation_goto,/", '', $goto);
-            $goto= preg_replace ( "/,$vacation_goto/", '', $goto);
-            $goto= preg_replace ( "/$vacation_goto/", '', $goto);
-            if($goto == '') {
-               $sql = "DELETE FROM $table_alias WHERE address = '$fUsername'";
-            }
-            else {
-               $sql = "UPDATE $table_alias SET goto='$goto',modified=NOW() WHERE address='$fUsername'";
-            }
-            $result = db_query($sql);
-            if ($result['rows'] != 1)
+            $result = db_query ("SELECT * FROM $table_alias WHERE address='$fUsername'");
+            if ($result['rows'] == 1)
             {
-               $error = 1;
+               $row = db_array ($result['result']);
+               $goto = $row['goto'];
+               //only one of these will do something, first handles address at beginning and middle, second at end
+               $goto= preg_replace ( "/$vacation_goto,/", '', $goto);
+               $goto= preg_replace ( "/,$vacation_goto/", '', $goto);
+               $goto= preg_replace ( "/$vacation_goto/", '', $goto);
+               if($goto == '') {
+                  $sql = "DELETE FROM $table_alias WHERE address = '$fUsername'";
+               }
+               else {
+                  $sql = "UPDATE $table_alias SET goto='$goto',modified=NOW() WHERE address='$fUsername'";
+               }
+               $result = db_query($sql);
+               if ($result['rows'] != 1)
+               {
+                  $error = 1;
+               }
             }
          }
       }
@@ -148,12 +160,15 @@ if ($_SERVER['REQUEST_METHOD'] == "POST"
    //Set the vacation data for $fUsername
    if (!empty ($fChange))
    {
-      $goto = '';
-      $result = db_query ("SELECT * FROM $table_alias WHERE address='$fUsername'");
-      if ($result['rows'] == 1)
+      if(!isset($CONF['no_vacation_alias']) || $CONF['no_vacation_alias']!='YES') 
       {
-         $row = db_array ($result['result']);
-         $goto = $row['goto'];
+         $goto = '';
+         $result = db_query ("SELECT * FROM $table_alias WHERE address='$fUsername'");
+         if ($result['rows'] == 1)
+         {
+            $row = db_array ($result['result']);
+            $goto = $row['goto'];
+         }
       }
       $Active = db_get_boolean(True);
       $notActive = db_get_boolean(False);
@@ -166,23 +181,25 @@ if ($_SERVER['REQUEST_METHOD'] == "POST"
       else {
           $result = db_query ("INSERT INTO $table_vacation (email,subject,body,domain,created,active) VALUES ('$fUsername','$fSubject','$fBody','$fDomain',NOW(),'$Active')");
       }
-
-      if ($result['rows'] != 1)
-      {
-         $error = 1;
-      }
-      if($goto == '') {
-         $goto = $vacation_goto;
-         $sql = "INSERT INTO $table_alias (goto, address, domain, modified) VALUES ('$goto', '$fUsername', '$fDomain', NOW())";
-      }
-      else {
-         $goto = $goto . "," . $vacation_goto;
-         $sql = "UPDATE $table_alias SET goto='$goto',modified=NOW() WHERE address='$fUsername'";
-      }
-      $result = db_query ($sql);
-      if ($result['rows'] != 1)
+      if(!isset($CONF['no_vacation_alias']) || $CONF['no_vacation_alias']!='YES') 
       {
-         $error = 1;
+         if ($result['rows'] != 1)
+         {
+            $error = 1;
+         }
+         if($goto == '') {
+            $goto = $vacation_goto;
+            $sql = "INSERT INTO $table_alias (goto, address, domain, modified) VALUES ('$goto', '$fUsername', '$fDomain', NOW())";
+         }
+         else {
+            $goto = $goto . "," . $vacation_goto;
+            $sql = "UPDATE $table_alias SET goto='$goto',modified=NOW() WHERE address='$fUsername'";
+         }
+         $result = db_query ($sql);
+         if ($result['rows'] != 1)
+         {
+            $error = 1;
+         }
       }
    }
 }
diff -uprN -x config.inc.php postfixadmin/model/VacationHandler.php postfixadmin/model/VacationHandler.php
--- postfixadmin/model/VacationHandler.php	2009-08-21 16:10:54.000000000 +0400
+++ postfixadmin/model/VacationHandler.php	2010-02-15 12:24:20.729327762 +0300
@@ -83,7 +83,7 @@ class VacationHandler {
      * @param string $subject
      * @param string $body
      */
-    function set_away($subject, $body) {
+    function set_away($subject, $body, $no_vacation_alias=False) {
         $this->remove(); // clean out any notifications that might already have been sent.
         // is there an entry in the vacaton table for the user, or do we need to insert?
         $table_vacation = table_by_key('vacation');
@@ -102,12 +102,15 @@ class VacationHandler {
             $domain = escape_string($tmp[1]);
             $result = db_query ("INSERT INTO $table_vacation (email,subject,body,domain,created,active) VALUES ('$username','$subject','$body','$domain',NOW(),'$active')");
         }
-
-        $ah = new AliasHandler($this->username); 
-        $aliases = $ah->get(true);
-        $vacation_address = $this->getVacationAlias();
-        $aliases[] = $vacation_address;
-        return $ah->update($aliases, '', false);
+        if ($no_vacation_alias) {
+            return True;
+        } else {
+            $ah = new AliasHandler($this->username); 
+            $aliases = $ah->get(true);
+            $vacation_address = $this->getVacationAlias();
+            $aliases[] = $vacation_address;
+            return $ah->update($aliases, '', false);
+        }
     }
 
     /**
diff -uprN -x config.inc.php postfixadmin/model/VacationHandler.php~ postfixadmin/model/VacationHandler.php~
--- postfixadmin/model/VacationHandler.php~	1970-01-01 03:00:00.000000000 +0300
+++ postfixadmin/model/VacationHandler.php~	2010-02-15 12:19:49.629292200 +0300
@@ -0,0 +1,128 @@
+<?php
+
+class VacationHandler {
+    protected $username = null;
+    function __construct($username) {
+        $this->username = $username;
+    }
+
+    /**
+     * Removes the autoreply alias etc for this user; namely, if they're away we remove their vacation alias and 
+     * set the vacation table record to false.
+     * @return boolean true on success.
+     */
+    function remove() {
+        $ah = new AliasHandler($this->username);
+        $aliases = $ah->get(true); // fetch all.
+        $new_aliases = array();
+        $table_vacation = table_by_key('vacation');
+        $table_vacation_notification = table_by_key('vacation_notification');
+
+        /* go through the user's aliases and remove any that look like a vacation address */
+        foreach($aliases as $alias) {
+            if(!$ah->is_vacation_address($alias)) {
+                $new_aliases[] = $alias;
+            }
+        }
+        $ah->update($new_aliases, '', false);
+
+        // tidy up vacation table.
+        $active = db_get_boolean(False);
+        $username = escape_string($this->username);
+        $result = db_query("UPDATE $table_vacation SET active = '$active' WHERE email='$username'");
+        $result = db_query("DELETE FROM $table_vacation_notification WHERE on_vacation='$username'");
+        /* crap error handling; oh for exceptions... */
+        return true;
+    }
+
+    /**
+     * @return boolean true indicates this server supports vacation messages, and users are able to change their own.
+     * @global array $CONF
+     */
+    function vacation_supported() {
+        global $CONF;
+        return $CONF['vacation'] == 'YES' && $CONF['vacation_control'] == 'YES';
+    }
+
+    /**
+     * @return boolean true if on vacation, otherwise false
+     * Why do we bother storing true/false in the vacation table if the alias dictates it anyway?
+     */
+    function check_vacation() {
+        $ah = new AliasHandler($this->username);
+        $aliases = $ah->get(true); // fetch all.
+        foreach($aliases as $alias) {
+            if($ah->is_vacation_address($alias)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Retrieve information on someone who is on vacation
+     * @return struct|boolean stored information on vacation - array(subject - string, message - string, active - boolean) 
+     * will return false if no existing data 
+     */
+    function get_details() {
+        $table_vacation = table_by_key('vacation');
+        $username = escape_string($this->username);
+
+        $sql = "SELECT * FROM $table_vacation WHERE email = '$username'";
+        $result = db_query($sql);
+        if($result['rows'] == 1) {
+            $row = db_array($result['result']);
+            $boolean = ($row['active'] == db_get_boolean(true));
+            return array( 'subject' => $row['subject'],
+                          'body' => $row['body'],
+                          'active'  => $boolean );
+        }
+        return false;
+    }
+    /**
+     * @param string $subject
+     * @param string $body
+     */
+    function set_away($subject, $body) {
+        $this->remove(); // clean out any notifications that might already have been sent.
+        // is there an entry in the vacaton table for the user, or do we need to insert?
+        $table_vacation = table_by_key('vacation');
+        $username = escape_string($this->username);
+        $body = escape_string($body);
+        $subject = escape_string($subject);
+
+        $result = db_query("SELECT * FROM $table_vacation WHERE email = '$username'");
+        $active = db_get_boolean(True);
+        // check if the user has a vacation entry already, if so just update it
+        if($result['rows'] == 1) {
+            $result = db_query("UPDATE $table_vacation SET active = '$active', body = '$body', subject = '$subject', created = NOW() WHERE email = '$username'");
+        }
+        else {
+            $tmp = preg_split ('/@/', $username);
+            $domain = escape_string($tmp[1]);
+            $result = db_query ("INSERT INTO $table_vacation (email,subject,body,domain,created,active) VALUES ('$username','$subject','$body','$domain',NOW(),'$active')");
+        }
+
+        $ah = new AliasHandler($this->username); 
+        $aliases = $ah->get(true);
+        $vacation_address = $this->getVacationAlias();
+        $aliases[] = $vacation_address;
+        return $ah->update($aliases, '', false);
+    }
+
+    /**
+     * Returns the vacation alias for this user. 
+     * i.e. if this user's username was roger@example.com, and the autoreply domain was set to
+     * autoreply.fish.net in config.inc.php we'd return roger#example.com@autoreply.fish.net
+     * @return string an email alias.
+     */
+    public function getVacationAlias() {
+        global $CONF;
+        $vacation_domain = $CONF['vacation_domain']; 
+        $vacation_goto = preg_replace('/@/', '#', $this->username); 
+        $vacation_goto = "{$vacation_goto}@{$vacation_domain}"; 
+        return $vacation_goto;
+    }
+}
+
+/* vim: set expandtab softtabstop=4 tabstop=4 shiftwidth=4: */
diff -uprN -x config.inc.php postfixadmin/templates/admin_list-domain.php postfixadmin/templates/admin_list-domain.php
--- postfixadmin/templates/admin_list-domain.php	2009-08-05 00:45:21.000000000 +0400
+++ postfixadmin/templates/admin_list-domain.php	2010-01-16 19:01:26.098731000 +0300
@@ -40,6 +40,7 @@ if (sizeof ($domain_properties) > 0)
    print "      <td>" . $PALANG['pAdminList_domain_backupmx'] . "</td>\n";
    print "      <td>" . $PALANG['pAdminList_domain_modified'] . "</td>\n";
    print "      <td>" . $PALANG['pAdminList_domain_active'] . "</td>\n";
+   if($CONF['spamassassin'] == 'YES') print "      <td>" . "Spamassassin". "</td>\n";
    print "      <td colspan=\"2\">&nbsp;</td>\n";
    print "   </tr>\n";
 
@@ -76,6 +77,9 @@ if (sizeof ($domain_properties) > 0)
          print "<td>" . $domain_properties[$i]['modified'] . "</td>";
          $active = ($domain_properties[$i]['active'] == db_get_boolean(true)) ? $PALANG['YES'] : $PALANG['NO'];
          print "<td><a href=\"edit-active-domain.php?domain=" . $domain_properties[$i]['domain'] . "\">" . $active . "</a></td>";
+	 if($CONF['spamassassin'] == 'YES') {
+            print "<td><a href=\"edit-spamassassin.php?domain=" . $domain_properties[$i]['domain'] . "\">" . "spamassassin" . "</a></td>";            
+         }
          print "<td><a href=\"edit-domain.php?domain=" . $domain_properties[$i]['domain'] . "\">" . $PALANG['edit'] . "</a></td>";
          print "<td><a href=\"delete.php?table=domain&delete=" . $domain_properties[$i]['domain'] . "\" onclick=\"return confirm ('" . $PALANG['confirm_domain'] . $PALANG['pAdminList_admin_domain'] . ": " . $domain_properties[$i]['domain'] . "')\">" . $PALANG['del'] . "</a></td>";
          print "</tr>\n";
diff -uprN -x config.inc.php postfixadmin/templates/edit-spamassassin.php postfixadmin/templates/edit-spamassassin.php
--- postfixadmin/templates/edit-spamassassin.php	1970-01-01 03:00:00.000000000 +0300
+++ postfixadmin/templates/edit-spamassassin.php	2010-01-27 01:53:49.964898000 +0300
@@ -0,0 +1,103 @@
+
+
+
+  
+
+<div id="edit_form">
+<form name="spamassassin" method="post">
+<table>
+
+   <tr>
+      <td colspan="3"><h3>Spamassassin settings</h3></td>
+   </tr>
+   <tr>
+      <td>User:</td>
+      <td><?php print $fUsername; ?></td>
+      <td>&nbsp;</td>
+   </tr>
+   <tr>
+      <td>Domain</td>
+      <td><?php print $fDomain; ?></td>
+      <td>&nbsp;</td>
+   </tr>
+
+   <tr>
+      <td>Required Hits</td>
+      <td>
+         <select name="required_hits">
+            <?php for($i=0;$i<=10;$i++) { ?>
+            <option value="<?php print $i ?>" <?php if($required_hits==$i) print 'selected'?>><?php print $i ?></option>
+            <?php } ?>
+         </select>
+      <td>&nbsp;</td>
+   </tr>
+
+   <tr>
+      <td>Subject Tag</td>
+      <td><textarea class="flat" cols="60" name="subject_tag" ><?php print htmlentities(stripslashes($subject_tag), ENT_QUOTES, 'UTF-8'); ?></textarea></td>
+      <td>&nbsp;</td>
+   </tr>
+
+   <tr>
+      <td>Whitelist From</td>
+      <td><textarea class="flat" cols="60" rows="6" name="whitelist_from" ><?php print htmlentities(stripslashes($whitelist_from), ENT_QUOTES, 'UTF-8'); ?></textarea></td>
+      <td>&nbsp;</td>
+   </tr>
+
+   <tr>
+      <td>Use Auto Whitelist</td>
+      <td><input class="flat" type="checkbox" name="use_auto_whitelist" <?php if($use_auto_whitelist) print "checked=\"checked\"" ?>/></td>
+      <td>&nbsp;</td>
+   </tr>
+
+   <tr>
+      <td colspan="3" class="hlp_center">
+        <input class="button" type="submit" name="spamassassin_submit" value="Save" />
+     </td>
+   </tr>
+
+   <tr>
+      <td colspan="3"><h3>Other parameters</h3></td>
+   </tr>
+   <tr>
+      <td>Preference</td>
+      <td><input class="flat" type="text" name="fPreference" value="" /></td>
+      <td><?php print $pEdit_mailbox_name_text; ?></td>
+   </tr>
+   <tr>
+      <td>Value</td>
+      <td><input class="flat" type="text" name="fValue" value="" /></td>
+      <td><?php print $pEdit_mailbox_name_text; ?></td>
+   </tr>
+
+</table>
+
+<table>
+	<tr>
+      <td>delete</td>
+      <td>user</td>
+      <td>prefid</td>
+      <td>preference</td>
+      <td>value</td>
+	<tr>
+<?php foreach($userprefs as $pref) { ?>
+   <tr>
+      <td><input class="flat" type="checkbox" name="fDeletePreferences[<?php print $pref['prefid'] ?>]" /></td>
+      <td><?php print $pref['username'] ?></td>
+      <td><?php print $pref['prefid'] ?></td>
+      <td><?php print $pref['preference'] ?></td>
+      <td><?php print $pref['value'] ?></td>
+   </tr>
+<?php } ?>
+
+   <tr>
+      <td colspan="5" class="hlp_center">
+        <input class="button" type="submit" name="spamassassin_submit_other" value="Save" />
+     </td>
+   </tr>
+   <tr>
+      <td colspan="5" class="standout"><?php print $tMessage; ?></td>
+   </tr>
+</table>
+</form>
+</div>
diff -uprN -x config.inc.php postfixadmin/templates/list-virtual.php postfixadmin/templates/list-virtual.php
--- postfixadmin/templates/list-virtual.php	2009-07-19 21:32:58.000000000 +0400
+++ postfixadmin/templates/list-virtual.php	2010-01-21 18:33:24.259286000 +0300
@@ -254,6 +254,7 @@ if (sizeof ($tMailbox) > 0) {
    $colspan=8;
    if ($CONF['vacation_control_admin'] == 'YES') $colspan=$colspan+1;
    if ($CONF['alias_control_admin'] == 'YES') $colspan=$colspan+1;
+   if($CONF['spamassassin'] == 'YES') $colspan=$colspan+1;
    print "<table id=\"mailbox_table\">\n";
    print "   <tr>\n";
    print "      <td colspan=\"$colspan\"><h3>" . $PALANG['pOverview_mailbox_title'] . "</h3></td>";
@@ -331,7 +332,9 @@ if (sizeof ($tMailbox) > 0) {
          {
             print "      <td><a href=\"edit-alias.php?address=" . urlencode ($tMailbox[$i]['username']) . "&domain=$fDomain" . "\">" . $PALANG['pOverview_alias_edit'] . "</a></td>\n";
          }
-
+         if($CONF['spamassassin'] == 'YES') {
+            print "     <td><a href=\"edit-spamassassin.php?username=" . urlencode ($tMailbox[$i]['username']) . "\">" . "spamassassin" . "</a></td>";            
+         }
          print "      <td><a href=\"edit-mailbox.php?username=" . urlencode ($tMailbox[$i]['username']) . "&domain=$fDomain" . "\">" . $PALANG['edit'] . "</a></td>\n";
          print "      <td><a href=\"delete.php?table=mailbox" . "&delete=" . urlencode ($tMailbox[$i]['username']) . "&domain=$fDomain" . "\"onclick=\"return confirm ('" . $PALANG['confirm'] . $PALANG['pOverview_get_mailboxes'] . ": ". $tMailbox[$i]['username'] . "')\">" . $PALANG['del'] . "</a></td>\n";
          print "   </tr>\n";
diff -uprN -x config.inc.php postfixadmin/users/vacation.php postfixadmin/users/vacation.php
--- postfixadmin/users/vacation.php	2009-04-25 21:58:56.000000000 +0400
+++ postfixadmin/users/vacation.php	2010-02-15 12:28:36.499292174 +0300
@@ -51,6 +51,9 @@ if ($_SERVER['REQUEST_METHOD'] == "GET")
 
     $details = $vh->get_details();
     if($details != false) {
+        if($CONF['vacation_base64']=='YES') {
+            $details['subject'] = base64_decode(substr($details['subject'],10,-2));
+        }
         $tSubject = $details['subject'];
         $tBody = $details['body'];
     }
@@ -70,6 +73,11 @@ if ($_SERVER['REQUEST_METHOD'] == "POST"
     }
 
     if (isset ($_POST['fSubject'])) $fSubject = $_POST['fSubject'];
+    if($CONF['vacation_base64']=='YES') {      
+       $fSubject   = "=?UTF-8?B?".base64_encode(escape_string ($fSubject))."?=";
+    } else {
+       $fSubject   = escape_string ($fSubject);
+    } 
     if (isset ($_POST['fBody']))    $fBody    = $_POST['fBody'];
     if (isset ($_POST['fAway'])) $fAway = escape_string ($_POST['fAway']);
     if (isset ($_POST['fBack'])) $fBack = escape_string ($_POST['fBack']);
@@ -83,7 +91,12 @@ if ($_SERVER['REQUEST_METHOD'] == "POST"
     // the user is going away - set the goto alias and vacation table as necessary.
     if (!empty ($fAway))
     {
-        if(!$vh->set_away($fSubject, $fBody)) {
+        if($CONF['no_vacation_alias']=='YES') {
+            $no_vacation_alias=True;
+        } else {
+            $no_vacation_alias=False;
+        }
+        if(!$vh->set_away($fSubject, $fBody, $no_vacation_alias)) {
             $error = 1;
             $tMessage = $PALANG['pUsersVacation_result_error'];
         }