The OpenNET Project / Index page

[ новости/++ | форум | wiki | теги ]

Индивидуальное квотирование виртуальных почтовых ящиков для Postfix (postfix quota limit patch virtual patch)


<< Предыдущая ИНДЕКС Поиск в статьях src Установить закладку Перейти на закладку Следующая >>
Ключевые слова: postfix, quota, limit, patch, virtual, patch,  (найти похожие документы)
From: -=VD= <vitaliy at tdauto.ru> Date: Mon, 20 Sep 2004 18:21:07 +0000 (UTC) Subject: Индивидуальное квотирование виртуальных почтовых ящиков для Postfix Оригинал: http://vitaliy.tdauto.ru Postfix 2. xx индивидуальное квотирование виртуальных почтовых ящиков Патч Postfix 2.xx + virtual + maildir + MySQL/PostgreSQL/.. (проверен postfix 2.0.11 - 2.0.16) © 2003 by -=VD= http://vitaliy.tdauto.ru Копию патча postfix2.0pvq.gz можно скачать здесь: ftp://ftp.opennet.ru/pub/net/mail/postfix_quota/ Задача: необходимо выставлять виртуальным пользователям Postfix "живущим", к примеру, в MySQL/PostgreSQL, индивидуальную квоту. Те патчи что я нашел на http://www.postfix.org все же меня не совсем устроили, т.к. захотелось решать все это на "лету" не принимая сообщения(сэкономим трафик), но при этом и отправитель и получатель должны уведомляться об этом "скорбном" факте. Для реализации задачи использовались: VMware + FreeBSD 4.8 + KDE + среда разработки KDevelop + оболочка отладчика KDbg + gdb 5.22 (gdb не меньше 5.22 т.к. все что младше, с KDbg у меня не работало). This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - изменения котрым подверглись исходники в конце документа - сам patch postfix2.0pvq.gz к сожалению утерян, сайт автора недоступен (проверен Postfix 2.0.11 - Postfix 2.0.16) BUGFIXES: 19.11.03 -> Устранена ошибка virtual_quota_user_message = пустое значение -> вызывало "fatal: bad string length (0 < 1):" 19.11.03 -> Устранена проблема при совместной установке с патчем ssl/tls -> они не любили друг друга(помирил) 19.11.03 -> Устранена ошибка вызывавшая возможное не срабатывание процедуры проверки квоты -> пользователь с локальным именем мог не квотироваться. Обсуждение всех возникавших проблем здесь: http://www.opennet.ru/openforum/vsluhforumID3/2827.html Отдельное спасибо HFSC помогшему устранить некоторые ошибки в логике кода. Что в результате получилось: После патча в main.cf, примерно как ниже, можно добавить 4 новых параметра: virtual_quota_user_message_limit = 2048 virtual_quota_user_bounce = yes virtual_quota_user_message = /../../../ваш _ файл virtual_mailbox_limit_maps = /../../../ваш _ файл Из них взаимосвязаны только между собой: virtual_quota_user_bounce = yes ( если = no 2 нижних просто неактуальны ) virtual_quota_user_message = /../../../ваш _ файл virtual_quota_user_message_limit = 2048 virtual_mailbox_limit_maps = /../../../ваш _ файл включает сам механизм квотирования, однако, при этом должны быть определены еще два postfix параметра: virtual_mailbox_maps = /../../../ваш_файл virtual_mailbox_base = /../../../ваш_файл ВНИМАНИЕ!!! Патч, на сегодняшний день, проверяет объем директори пользователя от имени дочернего процесса postfix(master) -> smtpd, поэтому права на maildir проверяемого пользователя должны совпадать с правами запуска smtpd. Упрощенно, если владельца virtual_mailbox_base определить отличного от mail_owner = ваше_значение, для патча не будет возможности выяснить текущий объем maildir пользователя и создать ему соответствующее уведомление. На работоспособности postfix в целом, это конечно вообще никак не отразится, однако квота отрабатывать не будет. Возможно через некоторое время я все же решу как устранить данное ограничение. Хотя в принципе, на сколько я знаю, для многих это типичная конфигурация postfix когда mail_owner и владелец на virtual_mailbox_base одинаковы, но... делать так делать, было бы время :) Ниже, моя врезка в main.cf: virtual_mailbox_base = /var/mail/virtual virtual_mailbox_maps = mysql:/usr/local/etc/postfix/sql/users.cf # Virtual quota virtual_mailbox_limit_maps = mysql:/usr/local/etc/postfix/sql/mail_limit.cf virtual_quota_user_bounce = yes virtual_quota_user_message = /usr/local/etc/postfix/sql/message.cf virtual_quota_user_message_limit = 2048 - virtual_mailbox_limit_maps по умолчанию = "" Путь к стандартному, вида postfix, файлу ..._maps. Опделяет дополнительное поле квоты. - virtual_quota_user_bounce по умолчанию = no Отсылать/нет служебное сообщение пользователю о превышении квоты. Изначально воспроизводится из шаблона, после чего в него добавляются строчки вида: 10.10.03 | mail from: aaa@bbb.cc - virtual_quota_user_message по умолчанию = "" Путь к файлу шаблона служебного сообщения. - virtual_quota_user_message_limit по умолчанию = 1024 Максимальный размер(байт) файла служебного сообщения. Реакция postfix на индивидуальную квоту, для отправителя/пользователья: Если индивидуальная квота пользователем превышена, отправителю еще до передачи данных ответ будет в виде: "4 хх /5 хх Sorry, the user <aaa@mydomain.com> has overdrawn diskspace quota, please try again later." (Захотите, переопределите это сообщение в исходниках.) Пользователь после превышения квоты получает одно служебное сообщение, созданное из шаблона и размером не больше чем virtual_quota_user_message_limit, в нем будет отражено когда и откуда почта не принята. В самом пиковом варианте, после превышения квоты, пользователь максимально сможет получить в свою maildir объем: (его квота - 1 байт) + (максимальный объем одного принимаемого сообщения), это связано с тем, что объем входящей почты в начале сеанса НЕ ВСЕГДА число определенное. Установка патча и настройка дополнительных параметров на примере postfix 2.0.16 + MySQL + Ваши_модули: Установку и настройку postfix для работы с виртуальными пользователями я здесь не описываю, т.к. этому уже посвящено несколько довольно подробных описаний, вот ссылки на некотрые из них: http://raven.elk.ru/unix/how-to/postfix2+cyrus-sasl2+kav+spamassassin+ courier-imap+tls+mysql+FreeBSD4/postfix2+cyrus-sasl2+kav+spamassassin+ courier-imap+tls+mysql+FreeBSD4.html http://www.opennet.ru/base/net/postgresql_postfix.txt.html Одним словом, я предполагаю что postfix у Вас уже установлен и настроен и Вы так же имеете представление зачем Вам этот патч :) Скачиваем отсюда патч postfix2.0pvq.gz распаковываем, cp /куда_вы_распаковали/postfix2.0pvq /там_где_исходник/postfix 2.0.16 cd /там_где_исходник/postfix 2.0.16 patch < postfix2.0pvq Патч может отработать в зависимости от версии postfix, c offset в 7 позиций (это конечно если патч наложен на "чистый" исходник). Пересоберем postfix с Вашими параметрами типа SASL MySQL и т.д. Собрался, значит, все готово. Дальше можно даже и не устанавливать его с помощью make install, т.е. если Вы пересобрали с параметрами что и в Вашем рабочем postfix, то изменилось всего 3 бинарника: ./src/smtpd/smtpd ./src/postconf/postconf ./src/cleanup/cleanup Ну а в прочем, make install хорошо сам во всем разберется (лишнего не удалит). В общем случае, осталось только отредактировать main.cf на предмет нововведений и все, однако, прежде чем его отредактировать, разбираемся на предмет текущего способа проверки пользователей и maildir. Тут дело в том, что патч отработает только при способе проверки(не путать с транспортом) определенном в postfix как virtual, плюс при условии, что у пользователей maildir, а не mailbox. На все другие варианты конфигурации и способы доставки этот патч влияния не оказывает. Разбираемся как обстоят дела с текущим способом проверки: telnet dew.tdauto.ru 25 220 dew.tdauto.ru ESMTP Postfix (2.0.16) HELO vitaliy.tdauto.ru 250 dew.tdauto.ru MAIL FROM: <any@aol.com> 250 Ok RCPT TO: <nomail@tdauto.ru> 550 <nomail@tdauto.ru>: User unknown in virtual mailbox table Я взял заведомо несуществующего у меня пользователя и ключевой момент тут как раз "... in virtual mailbox table " т.е. " virtual mailbox table " , это то что нам нужно, соответственно этот патч отработает. Другими словами, в качестве карты maildir пользователей, должен быть определен: virtual_mailbox_maps = /.../ваш _ файл А вот иные варианты ответов, в которых патч не сработает: 550 <nomail@tdauto.ru>: User unknown in local recipient table 550 <nomail@tdauto.ru>: User unknown in relay recipient table А вообще, по "старшинству", адрес postfix проверяет так: Сперва смотрит просто на наличие как такового в: 1. recipient_canonical_maps 2. canonical_maps 3. virtual_alias_maps Если адрес среди них есть, то postfix его принимает с 250 ОК. Если в тех что выше, ничего не найдено, проверяются с "пристрастием" и в жесткой зависимости от типа проверки: 4. local_recipient_maps 5. virtual_mailbox_maps 6. relay_recipient_maps Тут все наоборот, если способ проверки определен, а адреса нет, postfix ничего не принимает и дает отбой 4хх/5хх.... На сегодня этот патч отрабатывает virtual_alias_maps и virtual_mailbox_maps, остальные maps нет и смысла их включать в квотирование пока не вижу. Идем дальше - maildir: Для postfix, признак того, что у пользователей maildir - '/' в конце пути пользовательской почтовой директории. Кроме maildir, в самом конфиге, еще конечно должен быть: virtual_mailbox_base = /вашему/пути/к/maildir В общем случае: Квота сработает если: 1. virtual_mailbox_maps = /.../ваш _ файл 2. virtual_mailbox_base = /вашему/пути/к/maildir 3. virtual_mailbox_limit_maps = /../../../ваш _ файл 4. У пользователей путь к почте ../../vasa/ 5. Поле квоты в таблице не пустое. Не сработает если: 1. local_recipient_maps = /.../ваш _ файл 2. Или у пользователей путь к почте ../../vasa 3. Или virtual_mailbox_limit_maps закомментарено 4. Или поле квоты в таблице пустое Или этот адрес есть в: 5. recipient_canonical_maps = /.../ваш _ файл 6 . canonical_maps = /.../ваш _ файл Редакируем main.cf Добавляем туда примерно следующее(с Вашими путями конечно): # Virtual quota virtual_mailbox_limit_maps = mysql:/usr/local/etc/postfix/sql/mail_limit.cf virtual_quota_user_bounce = yes virtual_quota_user_message = /usr/local/etc/postfix/sql/message.cf virtual_quota_user_message_limit = 2048 Добавляем в MySQL таблицу users еще одно поле size типа varchar(у меня в users по классической схеме расположены адреса и maildir пользователей, Вы соответственно поле size называете как Вам хочется и заносите его в вашу таблицу). Заносим в size максимальный объем maildir юзера (чило в байтах). Соответственно создаем новый конфиг файл, для нашего нового поля size (в скобках мои значения) /usr/local/etc/postfix/sql/mail_limit.cf user = ваш (***) password = ваш (***) dbname = ваша (mail) table = ваша (users) select_field = ваш (size) where_field = ваш (login) additional_conditions = ваши (and expired = '0') hosts = ваш (localhost) Настраиваем шаблон сообщения message.cf для пользователя. Берем мой здесь и правим как Вам нужно, или создаем файл из шаблона что ниже: /usr/local/etc/postfix/sql/message.cf ---------------------------------------------- # # Шаблон сообщения для пользователя превысившего дисковую квоту. # Все строчки кроме тех что начинаются с # - значимые. # Return-Path: <postmaster@tdauto.ru> X-Original-To: _RCPT_TO_ Delivered-To: _RCPT_TO_ From: <postmaster@tdauto.ru> To: <_RCPT_TO_> Date: _MSG_TIME_ Content-Type: text/plain; charset=koi8-r Content-Transfer-Encoding: 8bit Subject: ВНИМАНИЕ ! Прием Вашей почты временно приостановлен. На данный момент, выделенное вам дисковое пространство полностью израсходовано Вашей входящей почтой. Прием на Ваш адрес временно прекращен и будет автоматически возобновлен, после того как Вы заберете накопившуюся почту. Допустимый объем Вашей директории: _USER_QUOTA_ байт Текущий объем Вашей директории: _UDIR_SIZE_ байт За это время Вам отсылали сообщения и были уведомлены о временной недоступности Вашего адреса: _MAIL_FROM_ С уважением: postmaster@tdauto.ru ---------------------------------------------- В результате отработки шаблона пользователь получит на свой ящик следующее: ---------------------------------------------- Return-Path: <postmaster@tdauto.ru> X-Original-To: vitaliy@tdauto.ru Delivered-To: vitaliy@tdauto.ru From: <postmaster@tdauto.ru> To: <vitaliy@tdauto.ru> Date: Mon, 10 Nov 2003 13:25:43 +0300 (MSK) Content-Type: text/plain; charset=koi8-r Content-Transfer-Encoding: 8bit Subject: ВНИМАНИЕ ! Прием Вашей почты временно приостановлен. На данный момент, выделенное вам дисковое пространство полностью израсходовано Вашей входящей почтой. Прием на Ваш адрес временно прекращен и будет автоматически возобновлен, после того как Вы заберете накопившуюся почту. Допустимый объем Вашей директории: 2096000 байт Текущий объем Вашей директории: 2099124 байт За это время Вам отсылали сообщения и были уведомлены о временной недоступности Вашего адреса: 07.10.03 13:25:57 (MSK) | mail from: any@yahoo.com 08.10.03 13:30:04 (MSK) | mail from: any@yahoo.com 09.10.03 15:00:45 (MSK) | mail from: any@mail.ru 10.11.03 15:21:37 (MSK) | mail from: any@any.ru 10.11.03 22:26:41 (MSK) | mail from: sssde@ttt.df С уважением: postmaster@tdauto.ru ---------------------------------------------- Конфиг готов, проверяем что вышло, предварительно режем у адреса квоту до 1 байта и больше 1 байта ему в maildir положим: telnet dew.tdauto.ru 25 220 dew.tdauto.ru ESMTP Postfix (2.0.16) HELO vitaliy.tdauto.ru 250 dew.tdauto.ru MAIL FROM: <any@aol.com> 250 Ok RCPT TO: <vitaliy@tdauto.ru> 550 Sorry, the user <vitaliy@tdauto.ru> has overdrawn diskspace quota, please try again later. RCPT TO: <vitaliy@tdauto.ru> 550 Sorry, the user <vitaliy@tdauto.ru> has overdrawn diskspace quota, please try again later. RCPT TO: <vitaliy@tdauto.ru> 550 Sorry, the user <vitaliy@tdauto.ru> has overdrawn diskspace quota, please try again later. Получили "550 Sorry..." следовательно отбой по квоте работает(намеренно отбой 3 раза, дабы проверить шаблон), проверяем ящик vitaliy@tdauto.ru - там должно быть уведомление из нашего шаблона с соответствующими цифрами, локальным временем и тремя: xx.xx.xx xx:xx:xx ( ХХХ ) | mail from: any@aol.com Как отработает квота по алиасам: Квота работает только с виртуальными алиасами т.е. с теми что находятся в virtual_alias_maps = /../ваш_файл К примеру в virtual_alias_maps есть адрес www@tdauto.ru и на нем, после всех алиасных переопределений, реально найдется 3 пользователя: user1@tdauto.ru user2@tdauto.ru user3@tdauto.ru Если все 3 пользователя существуют в virtual_mailbox_maps, то их проверят всех. Если кого-то там нет, следовательно, квота на него не распространяется (почта для него принимается). Если у кого нибудь из тех, кто есть в virtual_mailbox_maps квота превышена, то он почту не получит, а получит только служебное сообщение (все остальные получат почту). Если все 3 пользователя есть в virtual_mailbox_maps и все разом превысили квоту, отсылавший получит отбой: "550 Sorry, the user <www@tdauto.ru> has overdrawn diskspace quota, please try again later." и все 3 пользователя получат служебное сообщение. Как срабатывает ограничение на размер служебного сообщения: virtual_quota_user_message_limit = 2048 virtual_quota_user_bounce = yes Файл пишется непосредственно пользователю в maildir/new/ c уникальным именем и пока он там существует, в него будут добавляться строчки вида: xx.xx.xx xx:xx:xx ( ХХХ ) | mail from: < ххх @ хххх . хх > Я сделал так, дабы не плодить кучу мелких сообщений, по одному на каждый отбой. Для контроля позиции последней записи и текущего имени служебного файла, в корне maildir/ пользователя, создается технический файл maildir/postfix.user.quota (вроде это имя ни с кем другим не пересекается, по крайней мере мне не известно). Так вот, как только размер служебного сообшения в maildir/new/ становится = virtual_quota_user_message_limit, строчки в него перестают добавляться. Если же из maildir/new/ служебное сообщение перекочевало в maildir/cur/ например, пользователь просмотрел всю новую почту, но так ничего не забрав оставил ее на сервере, то на следующий отбой связанный с превышением квоты, в maildir/new/ создастся новое служебное сообщение, с новым уникальным именем, старое при этом соответственно останется в maildir/cur/ Контролировать размеры всех служебных сообщений во всех мыслимых директориях, я думаю, смысла нет... Вряд ли реально произойдет ситуация, что пользователь будет настойчиво не забирать живую почту с сервера и при этом баловаться ее просмотрами, наблюдая как на потихоньку (примерно байт по 700-900 в день в зависимости от шаблона и количества ему писавших) добавляются служебные сообщения. На этом с настройками квоты все. Думаю что особых затруднений все это вызвать не должно. В любом случае: vitaliy at tdauto.ru ПРИНИМАЕТСЯ ЛЮБАЯ КОНСТРУКТИВНАЯ КРИТИКА.
----------------------------------------------------------------------------------------------------------- ИЗМЕНЕНИЯ В ИСХОДНИКАХ ./src/global/mail_parms.h ------- Врезка ./src/global/mail_parms.h ------------------------------------------------------------------- /* * Virtual maildir quota. */ #define VAR_VIRT_MAILBOX_LIMIT_MAPS "virtual_mailbox_limit_maps" /* название в main.cf */ #define DEF_VIRT_MAILBOX_LIMIT_MAPS "" /* по умолчанию */ extern char *var_virt_mailbox_limit_maps; /* сама глобальная переменная */ #define VAR_VIRT_QUOTA_USER_BOUNCE "virtual_quota_user_bounce" #define DEF_VIRT_QUOTA_USER_BOUNCE 0 extern bool var_virt_quota_user_bounce; #define VAR_VIRT_QUOTA_USER_MSG "virtual_quota_user_message" #define DEF_VIRT_QUOTA_USER_MSG "" extern char *var_virt_quota_user_message; #define VAR_VIRT_QUOTA_USER_MSG_LIMIT "virtual_quota_user_message_limit" #define DEF_VIRT_QUOTA_USER_MSG_LIMIT "1024" extern char *var_virt_quota_user_message_limit; ------- Конец врезки./src/global/mail_parms.h --------------------------------------------------------------- ------- Врезка ./src/smtpd/smtpd.c ------------------------------------------------------------------------- /* * Virtual Quota. */ char *var_virt_mailbox_limit_maps; bool var_virt_quota_user_bounce; char *var_virt_quota_user_message; char *var_virt_quota_user_message_limit; char *var_virt_mailbox_base; . . /* /* смотрим main(....) находим там инициализацию *char и BOOL /* и добавляем туда новые переменные */ int main(int argc, char **argv) { . . . static CONFIG_BOOL_TABLE bool_table[] = { . . . VAR_VIRT_QUOTA_USER_BOUNCE, DEF_VIRT_QUOTA_USER_BOUNCE, &var_virt_quota_user_bounce, . . 0, }; static CONFIG_STR_TABLE str_table[] = { . . . VAR_VIRT_MAILBOX_LIMIT_MAPS, DEF_VIRT_MAILBOX_LIMIT_MAPS, &var_virt_mailbox_limit_maps, 0, 0, VAR_VIRT_QUOTA_USER_MSG, DEF_VIRT_QUOTA_USER_MSG, &var_virt_quota_user_message, 0, 0, VAR_VIRT_QUOTA_USER_MSG_LIMIT, DEF_VIRT_QUOTA_USER_MSG_LIMIT, &var_virt_quota_user_message_limit, 1, 0, VAR_VIRT_MAILBOX_BASE, DEF_VIRT_MAILBOX_BASE, &var_virt_mailbox_base, 0, 0, 0, }; } ------- Конец врезки ./src/smtpd/smtpd.c --------------------------------------------------------------- ------- Врезка ./src/smtpd/smtpd_check.c --------------------------------------------------------------- /* Необходимые дополнительные заголовочные файлы */ #include <sys/types.h> #include <sys/stat.h> #include <dirent.h> #include <mail_date.h> #include <been_here.h> #include <quote_822_local.h> #include <get_hostname.h> /* /* Новые функции и MAPS *virt_mailbox_limit_maps; */ static MAPS *virt_mailbox_limit_maps; ARGV *smtpd_resolve_virt_alias(const char *addr, MAPS *maps, int propagate); int smtpd_check_virt_user_quota(SMTPD_STATE *state, const char *addr); long check_dir_size(char *dirname); void smtpd_rw_quota_msg(ARGV *user_mess); void smtpd_rw_templ_msg(ARGV *user_mess, VSTREAM *fpr, VSTREAM *fpw); void smtpd_rw_user_msg(ARGV *user_mess, VSTREAM *fpw); VSTRING *smtpd_cp_key_msg(VSTRING *buffer, char *cut, char *paste); VSTRING *smtpd_quota_mail_date(time_t when); /* void smtpd_check_init(void) Добавляем инициализацию *virt_mailbox_limit_maps */ void smtpd_check_init(void) { . . . virt_mailbox_limit_maps = virtual8_maps_create(VAR_VIRT_MAILBOX_LIMIT_MAPS, var_virt_mailbox_limit_maps, DICT_FLAG_LOCK ); } /* static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient) */ /* Добавляем дополнительные вызовы проверки квоты. */ static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient) { ARGV *argv = 0; VSTRING *temp1; int cpp = 0; int stat_quota = 0; . . . /* Определяем схожий с Postfix макрос. */ #define MATCHV8(map, rcpt) \ checkv8_maps_find(state, recipient, map, rcpt) . . . /* Проверяем квоты по виртуальным алиасам. Тут немного добавилось но при этом функциональность прежнего кода полностью сохранена */ if (MATCH(rcpt_canon_maps, CONST_STR(reply->recipient)) || MATCH(canonical_maps, CONST_STR(reply->recipient))) return (0); if (MATCH(virt_alias_maps, CONST_STR(reply->recipient)) && (reply->flags & RESOLVE_CLASS_VIRTUAL) /* проверка на virtual ресольв */ && *var_virt_mailbox_maps && *var_virt_mailbox_base /* проверки всяческих катр */ && *var_virt_mailbox_limit_maps) { argv = smtpd_resolve_virt_alias(STR(reply->recipient), /* вызов ресольва альясов */ virt_alias_maps , 0); /* функция ресольва взята из postfix cleanup */ for (cpp = 0; cpp < argv->argc; cpp++) { /* в argv попадают реальные адреса */ if ((smtpd_check_virt_user_quota(state, argv->argv[cpp]))==0) /* вызов своей функции проверки квоты */ stat_quota = 1; } if (argv) argv_free(argv); if (stat_quota) return (0); /* 250 Ок если хоть один адрес не превысил квоту */ return(smtpd_check_reject(state, MAIL_ERROR_BOUNCE, /* отбой если все адреса превысили квоту */ "%d Sorry, the user <%s> has overdrawn diskspace quota, \ please try again later.", var_virt_mailbox_code, CONST_STR(reply->recipient))); } /* /* Дабы не портить общую картину, если наша проверка "промахнулась", оставляю /* старый вариант проверки virt_alias_maps, он сработает если /* reply->flags не попадает на RESOLVE_CLASS_VIRTUAL и т.д. */ if (MATCH(virt_alias_maps, CONST_STR(reply->recipient))) return (0); . . . /* Сразу после postfix проверки virtual юзеров, добавляем свою проверку квот */ if ((reply->flags & RESOLVE_CLASS_VIRTUAL) && *var_virt_mailbox_maps && *var_virt_mailbox_base && *var_virt_mailbox_limit_maps) { temp1 = vstring_alloc(100); quote_822_local(temp1, CONST_STR(reply->recipient)); if (smtpd_check_virt_user_quota(state, CONST_STR(temp1))) { /* вызов своей функции проверки квоты */ vstring_free(temp1); return(smtpd_check_reject(state, MAIL_ERROR_BOUNCE, /* отбой если адрес превысил квоту */ "%d Sorry, the user <%s> has overdrawn diskspace quota, \ please try again later.", var_virt_mailbox_code, CONST_STR(reply->recipient))); } vstring_free(temp1); } . . } //////////////////////////////////////// //////* Далее Новые функции *////////// /////////////////////////////////////// /* Ресольв альясов взят практически без изменений из cleanup, возвращает реальные адреса*/ ARGV *smtpd_resolve_virt_alias(const char *addr, MAPS *maps, int propagate) { ARGV *argv; ARGV *lookup; VSTRING *temp1 = vstring_alloc(100); int count; int i; int arg; BH_TABLE *been_here; char *saved_lhs; char *myname = "smtpd_resolve_virt_alias"; /* * Initialize. */ argv = argv_alloc(1); argv_add(argv, addr, ARGV_END); argv_terminate(argv); been_here = been_here_init(0, BH_FLAG_FOLD); /* * Rewrite the address vector in place. With each map lookup result, * split it into separate addresses, then rewrite and flatten each * address, and repeat the process. Beware: argv is being changed, so we * must index the array explicitly, instead of running along it with a * pointer. */ #define UPDATE(ptr,new) { myfree(ptr); ptr = mystrdup(new); } #define MAX_RECURSION 1000 #define MAX_EXPANSION 1000 #define STR vstring_str #define RETURN(x) { been_here_free(been_here); return (x); } for (arg = 0; arg < argv->argc; arg++) { if (argv->argc > MAX_EXPANSION) { msg_warn("%s: unreasonable %s map expansion size for %s", myname, maps->title, addr); break; } for (count = 0; /* void */ ; count++) { /* * Don't expand an address that already expanded into itself. */ if (been_here_check_fixed(been_here, argv->argv[arg]) != 0) break; if (count >= MAX_RECURSION) { msg_warn("%s: unreasonable %s map nesting for %s", myname, maps->title, addr); break; } quote_822_local(temp1, argv->argv[arg]); if ((lookup = mail_addr_map(maps, STR(temp1), propagate)) != 0) { saved_lhs = mystrdup(argv->argv[arg]); for (i = 0; i < lookup->argc; i++) { unquote_822_local(temp1, lookup->argv[i]); if (i == 0) { UPDATE(argv->argv[arg], STR(temp1)); } else { argv_add(argv, STR(temp1), ARGV_END); argv_terminate(argv); } /* * Allow an address to expand into itself once. */ if (strcasecmp(saved_lhs, STR(temp1)) == 0) been_here_fixed(been_here, saved_lhs); } myfree(saved_lhs); argv_free(lookup); } else if (dict_errno != 0) { msg_warn("%s: %s map lookup problem for %s", myname, maps->title, addr); vstring_free (temp1); RETURN(argv); } else { break; } } } vstring_free (temp1); RETURN(argv); } /* void smtpd_rw_quota_msg(ARGV *user_mess) */ /* открывает шаблон служебной мессаги или если она уже есть то открывает существующую*/ void smtpd_rw_quota_msg(ARGV *user_mess) { VSTREAM *fpr = 0; VSTREAM *fpw = 0; VSTREAM *fpt; VSTRING *buf = vstring_alloc(100); char *ftmp_quota; long msg_send_max = atol(var_virt_quota_user_message_limit); long msg_send_cur = 0; time_t starttime = time((time_t *) 0); struct stat st; /* user_mess->argv[0] from /* user_mess->argv[1] to /* user_mess->argv[2] user quota /* user_mess->argv[3] maidir size /* user_mess->argv[4] maildir */ ftmp_quota = concatenate(user_mess->argv[4], /* пробуем открыть технический файл */ "postfix.user.quota", (char *)0); /* и прочесть информацию о последней записи в служебном сообщении */ if ((fpt = vstream_fopen(ftmp_quota, O_RDONLY | O_EXCL, 0600)) != 0) { vstring_get_nonl(buf, fpt); /* вышло - читаем путь к сообщению */ vstream_fclose(fpt); fpw = vstream_fopen(STR(buf), O_RDWR | O_EXCL, 0600); /* закрываем ибо пока не нужен */ } myfree(ftmp_quota); if (fpw ==0) { /* технического файла нет, открываем шаблон */ if ((fpr = vstream_fopen(var_virt_quota_user_message, O_RDONLY, 0)) == 0) { msg_warn("cannot open file %s:", var_virt_quota_user_message); vstring_free(buf); return; } vstring_sprintf(buf, "%snew/%lu.VQUOTA%lxI%lx.%s", user_mess->argv[4], (unsigned long) starttime, (unsigned long) st.st_dev, (unsigned long) st.st_ino, get_hostname()); /* создаем уникальное имя файла */ /* из текущего времени + st_dev + st.st_ino + хост*/ if ((fpw = vstream_fopen(STR(buf), O_CREAT | O_RDWR | O_EXCL, 0600)) == 0) { /* создаем служебное сообщение*/ msg_warn("cannot create file %s:", STR(buf)); /* в юзер maildir/new/ */ vstring_free(buf); vstream_fclose(fpr); return; } smtpd_rw_templ_msg(user_mess, fpr, fpw); /* читаем шаблон - пишем сообщение */ vstring_free(buf); vstream_fclose(fpr); vstream_fclose(fpw); return; } if ( fpw && ((msg_send_cur = vstream_fseek(fpw, /* проверка объема служебного сообщения */ 0, SEEK_END)) < msg_send_max)) { vstream_fseek(fpw, 0, SEEK_SET); smtpd_rw_user_msg(user_mess, fpw); /* читаем сообщение, добавляем туда строчку*/ vstream_fclose(fpw); } vstring_free(buf); return; } /*VSTRING *smtpd_cp_key_msg(VSTRING *buffer, char *cut, char *paste) */ /* вырезает из строки то что нам нужно и вставляет то что хотим и возвращает строку*/ VSTRING *smtpd_cp_key_msg(VSTRING *buffer, char *cut, char *paste) { char *save; char *cp; while ((cp = strstr(vstring_str(buffer), cut))!=0) { save = mystrdup(cp + strlen(cut)); vstring_truncate (buffer, (VSTRING_LEN(buffer) - strlen(cp))); VSTRING_TERMINATE(buffer); vstring_sprintf_append(buffer, "%s%s", paste, save); myfree(save); } return (buffer); } /*void smtpd_rw_templ_msg(ARGV *user_mess, VSTREAM *fpr, VSTREAM *fpw)*/ /* читает шаблон, пишет в юзер maildir/new/ служебное сообщение*/ void smtpd_rw_templ_msg(ARGV *user_mess, VSTREAM *fpr, VSTREAM *fpw) { int cp; int cnt = 0; VSTRING *buffer; VSTRING *lt; VSTREAM *fpt; char *save; char *ftmp_quota; char *key; char *kmf = "_MAIL_FROM_"; /* ключевые поля подстановки*/ char *krt = "_RCPT_TO_"; char *kuq = "_USER_QUOTA_"; char *kus = "_UDIR_SIZE_"; char *ktm = "_MSG_TIME_"; /* user_mess->argv[0] from /* user_mess->argv[1] to /* user_mess->argv[2] user quota /* user_mess->argv[3] maidir size /* user_mess->argv[4] maildir */ buffer = vstring_alloc(100); do { cp = vstring_get(buffer, fpr); if (buffer->vbuf.data[0] == '#') /* режем комментарии*/ continue; if ((key = strstr(vstring_str(buffer), krt))!=0) /*ищем ключи и заменяем их на реальные значения*/ smtpd_cp_key_msg(buffer, krt, user_mess->argv[1]); if ((key = strstr(vstring_str(buffer), kuq))!=0) smtpd_cp_key_msg(buffer, kuq, user_mess->argv[2]); if ((key = strstr(vstring_str(buffer), kus))!=0) smtpd_cp_key_msg(buffer, kus, user_mess->argv[3]); if ((key = strstr(vstring_str(buffer), ktm))!=0) smtpd_cp_key_msg(buffer, ktm, (char *)mail_date(time((time_t *) 0))); if ((key = strstr(vstring_str(buffer), kmf))!=0) { ftmp_quota = concatenate(user_mess->argv[4], "postfix.user.quota", (char *)0); /*создаем технический файл postfix.user.quota*/ if ((fpt = vstream_fopen(ftmp_quota, O_CREAT | O_WRONLY | O_TRUNC, 0600)) == 0) { vstring_free(buffer); msg_warn("cannot create file %s:", ftmp_quota); myfree(ftmp_quota); return; } lt = smtpd_quota_mail_date(time((time_t *) 0)); save = concatenate(vstring_str(lt), " | mail from: ", user_mess->argv[0], (char *)0); smtpd_cp_key_msg(buffer, kmf, save); vstream_fprintf(fpt, "%s\n%d\n%d\n%d\n", VSTREAM_PATH(fpw), cnt, /* вписываем в postfix.user.quota*/ key - vstring_str(buffer), /* путь к служебному сообщению*/ (key - vstring_str(buffer)) + strlen(save)); /* номер строки с последним _MAIL_FROM_*/ myfree(ftmp_quota); /* позицию _MAIL_FROM_ в строке */ myfree(save); vstring_free(lt); vstream_fclose(fpt); } vstream_fputs(vstring_str(buffer), fpw); cnt++; } while(cp != VSTREAM_EOF); vstring_free(buffer); return; } /*VSTRING *smtpd_quota_mail_date(time_t when)*/ /* возвращает сформатированное локальное время и зону*/ VSTRING *smtpd_quota_mail_date(time_t when) { VSTRING *vp; struct tm *lt; vp = vstring_alloc(100); #define STRFTIME_FMT "%d.%m.%y %H:%M:%S" lt = localtime(&when); while (strftime(vstring_end(vp), vstring_avail(vp), STRFTIME_FMT, lt) == 0) VSTRING_SPACE(vp, 100); VSTRING_SKIP(vp); while (strftime(vstring_end(vp), vstring_avail(vp), " (%Z)", lt) == 0) VSTRING_SPACE(vp, 100); VSTRING_SKIP(vp); return (vp); } /*void smtpd_rw_user_msg(ARGV *user_mess, VSTREAM *fpw)*/ /*читает служебное сообщение если оно уже есть и добавляет туда новую строчку*/ void smtpd_rw_user_msg(ARGV *user_mess, VSTREAM *fpw) { int line, pos_s, pos_e = 0; int cnt = 0; int cp; int ch; long offset; VSTRING *buffer; VSTRING *lt; VSTREAM *fpt; char *ftmp_quota; char *patch; char *save; char *new; ftmp_quota = concatenate(user_mess->argv[4], /* открываем postfix.user.quota */ "postfix.user.quota", (char *)0); if ((fpt = vstream_fopen(ftmp_quota, O_RDWR | O_EXCL, 0600)) == 0) { myfree(ftmp_quota); return; } buffer = vstring_alloc(100); vstring_get_nonl(buffer, fpt); patch = mystrdup(STR(buffer)); /* путь к служебному сообщению*/ vstring_get_nonl(buffer, fpt); line = atoi(vstring_str(buffer)); /*номер строки*/ vstring_get_nonl(buffer, fpt); pos_s = atoi(vstring_str(buffer)); /*позиция начала последней добавки*/ vstring_get_nonl(buffer, fpt); pos_e = atoi(vstring_str(buffer)); /*позиция конца последней добавки*/ do { /* читаем до нужной строчки, сохраняем все что идет после нее и добавляем новую строку*/ cp = vstring_get(buffer, fpw); if (line == cnt) { lt = smtpd_quota_mail_date(time((time_t *) 0)); new = concatenate(vstring_str(lt), " | mail from: ", /*новая строка*/ user_mess->argv[0], (char *)0); vstring_free(lt); save = mystrndup(vstring_str(buffer)+pos_s, pos_e - pos_s); smtpd_cp_key_msg(buffer, save, new); vstream_fseek(fpt, 0, SEEK_SET); vstream_fflush(fpt); vstream_fprintf(fpt, "%s\n%d\n%d\n%d\n", patch, cnt+1, /* обновляем postfix.user.quota*/ pos_s, pos_s + strlen(new)); myfree(patch); myfree(save); myfree(new); offset = vstream_ftell(fpw); VSTRING_SKIP(buffer); while ((cp = VSTREAM_GETC(fpw))!= VSTREAM_EOF) VSTRING_ADDCH(buffer, cp); VSTRING_TERMINATE(buffer); vstream_fseek(fpw, offset, SEEK_SET); save = buffer->vbuf.data; while ((ch = *buffer->vbuf.data++) != 0) VSTREAM_PUTC(ch, fpw); buffer->vbuf.data = save; break; } cnt++; } while(cp != VSTREAM_EOF); vstring_free(buffer); myfree(ftmp_quota); vstream_fclose(fpt); return; } /* long check_dir_size(char *dirname) */ /* Взято из EXIM практически без изменений*/ /* возвращает объем директории в байтах*/ long check_dir_size(char *dirname) { DIR *dir; long sum = 0; struct dirent *ent; struct stat statbuf; dir = opendir(dirname); if (dir == NULL) { msg_warn("check_dir_size: cannot open directory : %s", dirname); return 0; } while ((ent = readdir(dir)) != NULL) { char *name = ent->d_name; VSTRING *buffer; if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue; buffer = vstring_alloc(1024); vstring_sprintf(buffer,"%s/%s",dirname,name); if (stat(vstring_str(buffer), &statbuf) < 0) { vstring_free(buffer); continue; } if ((statbuf.st_mode & S_IFREG) != 0) sum += statbuf.st_size; else if ((statbuf.st_mode & S_IFDIR) != 0) sum += check_dir_size(vstring_str(buffer)); vstring_free(buffer); } closedir(dir); if (msg_verbose) msg_info("check_dir_size: dir=%s sum=%ld", dirname, sum); return sum; } /* int smtpd_check_virt_user_quota(SMTPD_STATE *state, const char *addr) */ /* проверяет квоту юзера в virtual_mailbox_maps */ /* возвращает 0 если все в порядке и 1 если квота превышена*/ int smtpd_check_virt_user_quota(SMTPD_STATE *state, const char *addr) { ARGV *argv; char *user_quota; char *maildir; char *myname = "smtpd_check_virt_user_quota"; VSTRING *vb; int dir_size = 0; int size_quota = 0; #define GETV8(map, rcpt) \ checkv8_maps_find(state, rcpt, map, rcpt) #define LAST_CHAR(s) (s[strlen(s) - 1]) /* Sanity check. */ if (*var_virt_mailbox_base != '/') /* проверка virt_mailbox_base на корректность*/ msg_fatal("do not specify relative pathname: %s = %s", VAR_VIRT_MAILBOX_BASE, var_virt_mailbox_base); if ((maildir = GETV8(virt_mailbox_maps, addr)) !=0 ) maildir = concatenate(var_virt_mailbox_base, "/", maildir, (char *) 0); else return (0); /* Check maildir. */ if (LAST_CHAR(maildir) != '/') { /* проверка юзера на maildir*/ msg_info("%s do not specify maildir pathname: %s", myname, maildir); myfree(maildir); return (0); } if ((user_quota = mystrdup(GETV8(virt_mailbox_limit_maps, addr))) /*смотрим квоту*/ && ((size_quota = atoi(user_quota)) > 0)) { dir_size = check_dir_size(maildir); if ((dir_size >= 0) && (dir_size < size_quota)) { myfree(maildir); myfree (user_quota); return (0); } else { if (var_virt_quota_user_bounce { /* если надо, шлем служебное сообщение */ && *var_virt_quota_user_message) vb = vstring_alloc(100); argv = argv_alloc(1); vstring_sprintf(vb, "%d", dir_size); argv_add(argv, state->sender, ARGV_END); argv_add(argv, addr, ARGV_END); argv_add(argv, user_quota, ARGV_END); argv_add(argv, vstring_str(vb), ARGV_END); argv_add(argv, maildir, ARGV_END); argv_terminate(argv); smtpd_rw_quota_msg(argv); argv_free(argv); vstring_free(vb); } myfree(maildir); myfree (user_quota); return (1); } } return (0); } ------ Конец врезки ./src/smtpd/smtpd_check.c ------------------------------------------------------- ------ Врезка ./src/cleanup/cleanup.h --------------------------------------------------------------- /* * Virtual maildir quota. */ extern MAPS *cleanup_virt_mailbox_limit_maps; extern MAPS *cleanup_virt_mailbox_maps; ------ Конец врезки ./src/cleanup/cleanup.h ---------------------------------------------------------- В cleanup postfix сам ресольвит альясы и тут я просто не пропускаю превысившие квоту адреса ------ Врезка ./src/cleanup/cleanup_init.c ----------------------------------------------------------- /* Virtual maildir quota. */ #include <virtual8_maps.h> /* * Virtual maildir quota. */ char *var_virt_mailbox_limit_maps; char *var_virt_mailbox_maps; char *var_virt_mailbox_base; MAPS *cleanup_virt_mailbox_limit_maps; MAPS *cleanup_virt_mailbox_maps; CONFIG_STR_TABLE cleanup_str_table[] = { /* инициализация новых переменных */ . . . VAR_VIRT_MAILBOX_LIMIT_MAPS, DEF_VIRT_MAILBOX_LIMIT_MAPS, &var_virt_mailbox_limit_maps, 0, 0, VAR_VIRT_MAILBOX_BASE, DEF_VIRT_MAILBOX_BASE, &var_virt_mailbox_base, 0, 0, VAR_VIRT_MAILBOX_MAPS, DEF_VIRT_MAILBOX_MAPS, &var_virt_mailbox_maps, 0, 0, 0, }; void cleanup_pre_jail(char *unused_name, char **unused_argv) { . . . if (*var_virt_mailbox_limit_maps) cleanup_virt_mailbox_limit_maps = virtual8_maps_create(VAR_VIRT_MAILBOX_LIMIT_MAPS, var_virt_mailbox_limit_maps, DICT_FLAG_LOCK ); if (*var_virt_mailbox_maps) cleanup_virt_mailbox_maps = virtual8_maps_create(VAR_VIRT_MAILBOX_MAPS, var_virt_mailbox_maps, DICT_FLAG_LOCK); } ------ Конец врезки ./src/cleanup/cleanup_init.c --------------------------------------------------------------- ------ Врезка ./src/cleanup/cleanup_out_recipient.c --------------------------------------------------------------- /* Virtual maildir quota. */ #include <sys/types.h> /* opendir(3), stat(2) */ #include <sys/stat.h> /* stat(2) */ #include <dirent.h> /* opendir(3) */ #include <mail_addr_find.h> /*mail_addr_find*/ #include <stdlib.h> /*atoi*/ #include <stringops.h> /*concatenate*/ #include <msg.h> /*msg_info*/ /* Virtual maildir quota. */ int cleanup_check_virt_user_quota(char *recip); long check_dir_size(char *dirname); /* Здесь postfix ресольвит альясы */ void cleanup_out_recipient(CLEANUP_STATE *state, const char *orcpt, const char *recip) { . . . if (cleanup_virt_alias_maps == 0) { if (been_here(state->dups, "%s\n%s", orcpt, recip) == 0) { cleanup_out_string(state, REC_TYPE_ORCP, orcpt); cleanup_out_string(state, REC_TYPE_RCPT, recip); state->rcpt_count++; } } else { argv = cleanup_map1n_internal(state, recip, cleanup_virt_alias_maps, cleanup_ext_prop_mask & EXT_PROP_VIRTUAL); for (cpp = argv->argv; *cpp; cpp++) { if (been_here(state->dups, "%s\n%s", orcpt, *cpp) == 0) { /////////* вклинился здесь с вот этим*/////////////// if (*var_virt_mailbox_maps && *var_virt_mailbox_limit_maps && *var_virt_mailbox_base && (cleanup_check_virt_user_quota(*cpp))) continue; ///////////////////////////////////////////////////// cleanup_out_string(state, REC_TYPE_ORCP, orcpt); cleanup_out_string(state, REC_TYPE_RCPT, *cpp); state->rcpt_count++; } } argv_free(argv); } } /* long check_dir_size(char *dirname)*/ /* Подсчет директории, тут все без изменений как и в smtpd_check.c*/ long check_dir_size(char *dirname) { DIR *dir; long sum = 0; struct dirent *ent; struct stat statbuf; dir = opendir(dirname); if (dir == NULL) return 0; while ((ent = readdir(dir)) != NULL) { char *name = ent->d_name; VSTRING *buffer; if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue; buffer = vstring_alloc(1024); vstring_sprintf(buffer,"%s/%s",dirname,name); if (stat(vstring_str(buffer), &statbuf) < 0) { vstring_free(buffer); continue; } if ((statbuf.st_mode & S_IFREG) != 0) sum += statbuf.st_size; else if ((statbuf.st_mode & S_IFDIR) != 0) sum += check_dir_size(vstring_str(buffer)); vstring_free(buffer); } closedir(dir); return sum; } /* int cleanup_check_virt_user_quota(char *recip) */ /* все как и в smtpd_check.c за исключением того что служебное сообщение теперь не шлется*/ int cleanup_check_virt_user_quota(char *recip) { char *maildir; char *user_quota; char *myname = "cleanup_check_virt_user_quota"; int dir_size = 0; int size_quota = 0; #define GETV8(map, rcpt) \ (char *)mail_addr_find(map, rcpt, (char **) 0) #define LAST_CHAR(s) (s[strlen(s) - 1]) /* Sanity check. */ if (*var_virt_mailbox_base != '/') msg_fatal("do not specify relative pathname: %s = %s", VAR_VIRT_MAILBOX_BASE, var_virt_mailbox_base); if ((maildir = GETV8(cleanup_virt_mailbox_maps, recip)) !=0 ) maildir = concatenate(var_virt_mailbox_base, "/", maildir, (char *) 0); else return (0); /* Check maildir. */ if (LAST_CHAR(maildir) != '/') { msg_info("%s do not specify maildir pathname: %s", myname, maildir); myfree(maildir); return (0); } if ((user_quota = GETV8(cleanup_virt_mailbox_limit_maps, recip)) && ((size_quota = atoi(user_quota)) > 0)) { dir_size = check_dir_size(maildir); if ((dir_size >= 0) && (dir_size < size_quota)) { myfree(maildir); return (0); } else { myfree(maildir); return (1); } } return (0); } ------ Конец врезки ./src/cleanup/cleanup_out_recipient.c ---------------------------------------------------------------

<< Предыдущая ИНДЕКС Поиск в статьях src Установить закладку Перейти на закладку Следующая >>

Обсуждение [ RSS ]
 
  • 1.1, Zmey, 08:42, 07/10/2004 [ответить] [смотреть все]
  • +/
    Ссылки по теме:

    http://web.onda.com.br/nadal/

    Патч который в последних версиях Postfix идет в поставке, и может быть выбран при установке из конфигуратора Postfix.

     
  • 1.2, Gezm0, 21:57, 22/04/2005 [ответить] [смотреть все]
  • +/
    Сайт автора недоступен, самого автора найти нет возможности =( Кто-нибудь прикручивал этот патч к текущей версии т.е. postfix 2.2 ?
     
  • 1.3, Антон, 19:06, 27/12/2005 [ответить] [смотреть все]
  • +/
    Ну в моем случае было проще написать свой патч, для проверки сободного места в базе и отлупа если письмо больше свободного места.

    то есть я оставил только команду вида:
    virtual_mailbox_limit_maps = cdb:/etc/postfix/db/users_quota

    работает на ура.

     

    Ваш комментарий
    Имя:         
    E-Mail:      
    Заголовок:
    Текст:





      Закладки на сайте
      Проследить за страницей
    Created 1996-2017 by Maxim Chirkov  
    ДобавитьРекламаВебмастеруГИД  
    Hosting by Ihor